Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow accessing public shares with CORS #5

Open
pojntfx opened this issue Apr 29, 2021 · 23 comments
Open

Allow accessing public shares with CORS #5

pojntfx opened this issue Apr 29, 2021 · 23 comments
Labels

Comments

@pojntfx
Copy link

pojntfx commented Apr 29, 2021

Explain the Problem

I'm trying to access a public WebDAV share as described in https://docs.nextcloud.com/server/20/user_manual/en/files/access_webdav.html#accessing-public-shares-over-webdav.

Steps to Reproduce

  1. Add origins using web interface: http://localhost:15755,http://localhost:15755/
  2. Make fetch request with hash of share as username:
fetch("https://nx904.your-storageshare.de/public.php/webdav/", {
  "headers": {
    "accept": "application/xml,text/xml",
    "authorization": "Basic cEZBOFBUd0NtSnlTVERqOg==",
    "content-type": "application/xml;charset=UTF-8",
    "depth": "1",
    "sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"90\", \"Google Chrome\";v=\"90\"",
    "sec-ch-ua-mobile": "?0"
  },
  "referrer": "http://localhost:15755/",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": "<d:propfind xmlns:d='DAV:'>\n\t\t\t<d:prop>\n\t\t\t\t<d:displayname/>\n\t\t\t\t<d:resourcetype/>\n\t\t\t\t<d:getcontentlength/>\n\t\t\t\t<d:getcontenttype/>\n\t\t\t\t<d:getetag/>\n\t\t\t\t<d:getlastmodified/>\n\t\t\t</d:prop>\n\t\t</d:propfind>",
  "method": "PROPFIND",
  "mode": "cors",
  "credentials": "include"
});
  1. The request fails due to CORS:
Access to fetch at 'https://nx904.your-storageshare.de/public.php/webdav/' from origin 'http://localhost:15755' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
wasm_exec.js:399 PROPFIND https://nx904.your-storageshare.de/public.php/webdav/ net::ERR_FAILED

It works using plain curl:

$ curl 'https://nx904.your-storageshare.de/public.php/webdav/' \
>   -X 'PROPFIND' \
>   -H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"' \
>   -H 'sec-ch-ua-mobile: ?0' \
>   -H 'authorization: Basic cEZBOFBUd0NtSnlTVERqOg==' \
>   -H 'content-type: application/xml;charset=UTF-8' \
>   -H 'accept: application/xml,text/xml' \
>   -H 'Referer: http://localhost:15755/' \
>   -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36' \
>   -H 'depth: 1' \
>   --data-raw $'<d:propfind xmlns:d=\'DAV:\'>\n\u0009\u0009\u0009<d:prop>\n\u0009\u0009\u0009\u0009<d:displayname/>\n\u0009\u0009\u0009\u0009<d:resourcetype/>\n\u0009\u0009\u0009\u0009<d:getcontentlength/>\n\u0009\u0009\u0009\u0009<d:getcontenttype/>\n\u0009\u0009\u0009\u0009<d:getetag/>\n\u0009\u0009\u0009\u0009<d:getlastmodified/>\n\u0009\u0009\u0009</d:prop>\n\u0009\u0009</d:propfind>' \
>   --compressed
<?xml version="1.0"?>
<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns"><d:response><d:href>/public.php/webdav/</d:href><d:propstat><d:prop><d:resourcetype><d:collection/></d:resourcetype><d:getetag>&quot;608923d732f31&quot;</d:getetag><d:getlastmodified>Wed, 28 Apr 2021 08:59:03 GMT</d:getlastmodified></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat><d:propstat><d:prop><d:displayname/><d:getcontentlength/><d:getcontenttype/></d:prop><d:status>HTTP/1.1 404 Not Found</d:status></d:propstat></d:response><d:response><d:href>/public.php/webdav/2021/</d:href><d:propstat><d:prop><d:resourcetype><d:collection/></d:resourcetype><d:getetag>&quot;608923d732f31&quot;</d:getetag><d:getlastmodified>Wed, 28 Apr 2021 08:59:03 GMT</d:getlastmodified></d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat><d:propstat><d:prop><d:displayname/><d:getcontentlength/><d:getcontenttype/></d:prop><d:status>HTTP/1.1 404 Not Found</d:status></d:propstat></d:response></d:multistatus>

System Information

  • WebAppPassword app version: 21.3.0
  • Nextcloud version: 20.0.9
  • Cron type: / (Hosted by Hetzner)
  • PHP version: / (Hosted by Hetzner)
  • Database and version: / (Hosted by Hetzner)
  • Browser and version: Chrome 90, Firefox 88
  • Distribution and version: Fedora Linux 34

Contents of nextcloud/data/nextcloud.log

Paste output here

(No access to logs, hosted by Hetzner)

Contents of Browser Error Console

Read http://ggnome.com/wiki/Using_The_Browser_Error_Console if you are unsure what to put here

Access to fetch at 'https://nx904.your-storageshare.de/public.php/webdav/' from origin 'http://localhost:15755' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
wasm_exec.js:399 PROPFIND https://nx904.your-storageshare.de/public.php/webdav/ net::ERR_FAILED
@pbek
Copy link
Member

pbek commented Apr 29, 2021

Contents of nextcloud/data/nextcloud.log

Don't you have logs in your Nextcloud admin console? Something like https://nx904.your-storageshare.de/settings/admin/logging

@pbek
Copy link
Member

pbek commented Apr 29, 2021

fetch("https://nx904.your-storageshare.de/public.php/webdav/", {

where are you making this request? In a browser on your http://localhost:15755 webpage?

@pbek
Copy link
Member

pbek commented Apr 29, 2021

It works using plain curl:

curl hasn't the same restrictions as a web-browser

@pbek pbek added the support label Apr 29, 2021
@pbek
Copy link
Member

pbek commented Apr 29, 2021

wasm_exec.js

Are you using web assembly to run the fetch? I've no idea what the browser restrictions are in that case...

@pojntfx
Copy link
Author

pojntfx commented Apr 29, 2021

@pbek Thanks for the quick response! Logs are just empty, even with an admin account - hosting restrictions. For the questions:

  1. The request is made from origin http://localhost:15755
  2. Exactly - I just wanted to isolate it down to a CORS and not an authentication etc. problem
  3. Yes, it is WASM (Go)! There are no restrictions here though and running the plain fetch call/calling the DOM yields the same error.

I've also found a (hacky) workaround; the app I'm building (https://github.com/pojntfx/growlapse) uses a Go agent to export IoT image data to a WebDAV share, and I'm then trying to visualize this data with a WASM frontend. Using read-only guest users I was able to just share access to a guest user and have functionally the same effect (read-only access to a directory from the browser): https://github.com/pojntfx/growlapse/blob/main/docs/backend_nextcloud.md.

A working version of my hacky workaround (using a read-only guest user; trying to access /; see the "Network" tab of your browser for the successful request): https://pojntfx.github.io/growlapse/?webDAVPassword=Growlapse&webDAVURL=https%3A%2F%2Fnx904.your-storageshare.de%2Fremote.php%2Fdav%2Ffiles%2Ffelix.pojtinger%40gmail.com%2F&webDAVUsername=felix.pojtinger%40gmail.com

@pbek
Copy link
Member

pbek commented Apr 29, 2021

Like I said, I've no idea how CORS is handled inside WASM in a browser.
And I don't think that you simply can spoof the referrer in the fetch command, like you did above.
What happens when you remove the two referrer parameters?

And did you try the example from https://github.com/digital-blueprint/webapppassword#example? It also uses fetch.

@pbek
Copy link
Member

pbek commented Apr 29, 2021

After all CORS is something your web browser enforces, not the Nextcloud server. webapppassword only attempts to set some headers (hoping that hetzner doesn't override them, you should see those preflight requests in your web inspector) that tells your browser that it is ok to let the request through when it is coming from a certain origin.

@pojntfx
Copy link
Author

pojntfx commented Apr 29, 2021

CORS inside WASM is the same as CORS w/o WASM, it simply binds to the DOM API - there should be no difference, any manually executing the request yields the same error. The command above is from the developer tools, not manually constructed by me - it's the request that Chrome sends. Removing the headers doesn't change anything, as you've already pointed out. The example you've given doesn't allow me to specify a user manually, but rather seems to use OAuth, which is not how the public share API works (Nextcloud wants the ID of the share as the username and no password)

@pbek
Copy link
Member

pbek commented Apr 29, 2021

CORS inside WASM is the same as CORS w/o WASM, it simply binds to the DOM API - there should be no difference

then what do you see in the network log of your browser? there should be response headers with access-control-allow-origin telling you what origins are allowed and there should be OPTION requests done by your browser...

@pbek
Copy link
Member

pbek commented Apr 29, 2021

but rather seems to use OAuth

username/password in https://github.com/digital-blueprint/webapppassword/blob/master/docs/example/index.html#L100

@pojntfx
Copy link
Author

pojntfx commented Apr 29, 2021

username/password in https://github.com/digital-blueprint/webapppassword/blob/master/docs/example/index.html#L100

Thanks; setting it there doesn't seem to fix it though:

const webdavUrl = "https://nx904.your-storageshare.de/public.php/webdav";

    const headers = new Headers();
    headers.set('Authorization', 'Basic ' + btoa("wWfAA3sMSoyfgJB" + ":"));

    fetch(webdavUrl + "/", {
        method: 'PROPFIND',
        cache: 'no-cache',
        headers: headers
    })

image

image

image

Using the plain example works of course, but the WebDAV URL returned by the app is https://nx904.your-storageshare.de/remote.php/dav/files/felix.pojtinger--administrator/ which is of course not the public share one wants to access.

Pasting davs://[email protected]/public.php/webdav into Nautilus' address field and using an empty password works and returns the read-only share.

@pbek
Copy link
Member

pbek commented Apr 29, 2021

What are the response and request headers of those two red requests?

@pojntfx
Copy link
Author

pojntfx commented Apr 29, 2021

For the first one:

image

For the second one:

image

@pbek
Copy link
Member

pbek commented Apr 29, 2021

You don't get the response headers... They are supposed to look like:

image

@pbek
Copy link
Member

pbek commented Apr 29, 2021

Maybe you want to git clone the repository and spin up the docker container from https://github.com/digital-blueprint/webapppassword/tree/master/docker to test your wasm and js locally...

@pojntfx
Copy link
Author

pojntfx commented Apr 29, 2021

Thanks - just tried it out! Same result ("Access to fetch at 'http://localhost:8081/public.php/webdav/' from origin 'http://localhost:5000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."). Is this app set up to handle public shares? Basic auth and accessing them from /remote.php/dav/files works fine, it's just that if one starts to request from /public.php/webdav/ that things start to break - this is required though as using the "private' endpoint leaks user credentials, which even with a guest user means that externals can change user info. The log (from Docker, make show-log and web log) don't show anything out of the ordinary:

app_1  | 172.18.0.1 - - [29/Apr/2021:16:23:33 +0000] "POST /index.php/apps/text/public/session/sync HTTP/1.1" 200 1034 "-" "Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"
app_1  | 172.18.0.1 - - [29/Apr/2021:16:23:38 +0000] "OPTIONS /public.php/webdav/ HTTP/1.1" 401 1900 "http://localhost:5000/" "Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36"

@pbek
Copy link
Member

pbek commented Apr 29, 2021

I've haven't researched if public shares are handled differently.
The header magic happens in: https://github.com/digital-blueprint/webapppassword/blob/master/lib/Connector/Sabre/CorsPlugin.php

Maybe you can find a different hook (like in https://github.com/digital-blueprint/webapppassword/blob/master/lib/Connector/Sabre/CorsPlugin.php#L33) for public shares.

@pojntfx
Copy link
Author

pojntfx commented Apr 29, 2021

I'll try my best! Haven't written PHP before so I'll see how it goes :)

@pbek
Copy link
Member

pbek commented Apr 29, 2021

If all goes well you just need to write one line after https://github.com/digital-blueprint/webapppassword/blob/master/lib/Connector/Sabre/CorsPlugin.php#L33 to execute setCorsHeaders. 😁

Good luck, have fun! 🥳

@pbek
Copy link
Member

pbek commented Apr 29, 2021

And you can develop directly with the docker container provided in https://github.com/digital-blueprint/webapppassword/tree/master/docker. ;)

@pojntfx
Copy link
Author

pojntfx commented Apr 29, 2021

Alright, I've been able to set up the dev environment - beforeMethod is the correct event, setCorsHeaders seems to get called. The issue is: No matter what I do, changes to headers etc. don't seem to be reflected. I've found this: https://github.com/owncloud/core/blob/master/apps/dav/lib/Connector/Sabre/CorsPlugin.php which I tried to port but it didn't seem to fix the issue. Any ideas? Is there maybe a way to trace incoming requests so that I can check what may be modifying/changing the headers?

@pbek
Copy link
Member

pbek commented Apr 30, 2021

Did you also check if https://github.com/digital-blueprint/webapppassword/blob/master/lib/Connector/Sabre/CorsPlugin.php#L58 is really executed for your request?
You could try to ask in the nextcloud-dev IRC on freenode.

@pojntfx
Copy link
Author

pojntfx commented Apr 30, 2021

I put an echo there, which crashed it when making the request (something with the headers) so yeah I think so. I've removed the condition, no change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants