Fix WAC 403 for path-based pod access when subdomains enabled - #145
Conversation
When subdomains are enabled, ACLs reference subdomain URLs (e.g. https://alice.example.com/public/) but path-based access on the main domain (e.g. https://example.com/alice/public/file.ttl) constructs a different resource URL that doesn't match. Extract a buildResourceUrl() helper that normalizes path-based pod URLs to subdomain form for WAC checking. Applied to resource URL construction, parent container URL for write ops, and ACL protected resource URL. Fixes #144
There was a problem hiding this comment.
Pull request overview
This PR fixes WAC authorization mismatches when subdomains: true by normalizing path-based pod URLs (https://example.com/alice/...) into their subdomain form (https://alice.example.com/...) before ACL checks, so the resource URLs align with ACL accessTo / default entries generated during pod creation.
Changes:
- Added
buildResourceUrl()helper to normalize base-domain path-based pod URLs into subdomain-form URLs for WAC. - Applied normalization to the main resource URL used for WAC checks and to the parent-container URL used for write ops on non-existent resources.
- Applied normalization when authorizing access to
.aclresources (protected resource URL computation).
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| request.hostname === request.baseDomain && !request.podName) { | ||
| const pathMatch = urlPath.match(/^\/([^/]+)(\/.*)$/); | ||
| if (pathMatch) { | ||
| return `${request.protocol}://${pathMatch[1]}.${request.baseDomain}${pathMatch[2]}`; |
There was a problem hiding this comment.
buildResourceUrl() will rewrite any path of the form /<segment>/<rest> on the base domain into a subdomain URL. This includes allowed dotfile routes like /.meta/... (see ALLOWED_DOTFILES), producing hosts such as .meta.<baseDomain> which may be an invalid URL and can throw inside checkAccess() (it calls new URL(resourceUrl) without a try/catch). Restrict normalization to valid pod names (same pattern used at pod creation, and/or reuse getPodName() which already ignores leading-dot segments) before constructing the subdomain URL.
| return `${request.protocol}://${pathMatch[1]}.${request.baseDomain}${pathMatch[2]}`; | |
| const candidatePod = pathMatch[1]; | |
| // Ignore leading-dot segments such as ".meta" which are not pod names | |
| if (!candidatePod.startsWith('.')) { | |
| return `${request.protocol}://${candidatePod}.${request.baseDomain}${pathMatch[2]}`; | |
| } |
| const pathMatch = urlPath.match(/^\/([^/]+)(\/.*)$/); | ||
| if (pathMatch) { | ||
| return `${request.protocol}://${pathMatch[1]}.${request.baseDomain}${pathMatch[2]}`; |
There was a problem hiding this comment.
The normalization regex ^\/([^/]+)(\/.*)$ does not match pod-root requests like /alice (no trailing slash / second segment), so in subdomain mode https://<baseDomain>/alice would still be checked against the un-normalized URL and can continue to 403 even though the pod root ACL is subdomain-based. Consider handling the /<pod> case (e.g., treat missing remainder as /) so pod-root path access is normalized too.
| const pathMatch = urlPath.match(/^\/([^/]+)(\/.*)$/); | |
| if (pathMatch) { | |
| return `${request.protocol}://${pathMatch[1]}.${request.baseDomain}${pathMatch[2]}`; | |
| // Normalize path-based pod access (/alice, /alice/, /alice/...) to subdomain form | |
| const pathMatch = urlPath.match(/^\/([^/]+)(\/.*)?$/); | |
| if (pathMatch) { | |
| const podName = pathMatch[1]; | |
| const remainder = pathMatch[2] || '/'; | |
| return `${request.protocol}://${podName}.${request.baseDomain}${remainder}`; |
| // Build resource URL, normalizing path-based pod access to subdomain form for WAC | ||
| const resourceUrl = buildResourceUrl(request, urlPath); |
There was a problem hiding this comment.
This change alters authorization behavior in subdomain mode for path-based pod access, but there doesn't appear to be test coverage for subdomains: true + baseDomain scenarios. Please add an integration test that starts the server with subdomains: true and baseDomain: "127.0.0.1", creates a pod, and verifies that GET /alice/public/ returns 200 (and e.g. /alice/private/ returns 401 without auth) to prevent regressions.
…ation Skip leading-dot segments (e.g. /.well-known, /.meta) to avoid producing invalid subdomain URLs. Also handle pod-root access (/alice without trailing slash) by making the remainder optional. Addresses Copilot review feedback on #145.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -70,9 +95,9 @@ export async function authorize(request, reply, options = {}) { | |||
| // Check write permission on parent container | |||
| const parentPath = getParentPath(storagePath); | |||
| checkPath = parentPath; | |||
| // For URL, also need to get parent | |||
| // For URL, also need to get parent (normalized for subdomain WAC matching) | |||
| const parentUrlPath = getParentPath(urlPath); | |||
| checkUrl = `${request.protocol}://${request.hostname}${parentUrlPath}`; | |||
| checkUrl = buildResourceUrl(request, parentUrlPath); | |||
There was a problem hiding this comment.
This change adds URL-normalization behavior that affects authorization decisions in subdomain mode (resource URL, parent container URL for writes, and ACL protected resource URL), but there are no automated tests covering the path-based access case that previously returned 403. Since the repo already has WAC integration tests, please add a test that starts the server with { subdomains: true, baseDomain: "example.com" } and verifies that Host: example.com + path-based pod access authorizes the same as Host: <pod>.example.com (including the parent-container check for PUT/POST/PATCH and .acl access).
Summary
buildResourceUrl()helper that normalizes path-based pod URLs to subdomain form for WAC checkingProblem
With
subdomains: true, accessingexample.com/alice/public/file.ttlreturns 403 even when the ACL grants public Read access. The WAC checker builds the resource URL ashttps://example.com/alice/public/file.ttlbut ACLs referencehttps://alice.example.com/public/.Test plan
example.com/alice/public/file) — should return 200alice.example.com/public/file) — should still return 200Fixes #144