-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Support for CORS on OCS and Webdav APIs + domain whitelisting #28457
Conversation
core/Controller/CloudController.php
Outdated
@@ -40,6 +40,11 @@ public function __construct($appName, IRequest $request) { | |||
* @return array | |||
*/ | |||
public function getCapabilities() { | |||
header("Access-Control-Allow-Origin: *"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use * as a wildcard domain list. We should use the value of the origin header in the initial request. A much better solution ist to have a whitelist with domains that are allowed to request content from the other domain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Peter-Prochaska ,
In this Pull Request, I was first trying to manually enable CORS for all (The * header) and I later attempt to make an OC app, which would maintain a table of the authenticated users authenticated via OAuth2 and then enable CORS for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to discuss about a concept for this.
I'm not sure if CORS can be used per authenticated user/domain. If it can, then the OPTIONS call might need to be pre-authenticated to be able to go through and receive the headers.
You'd then need a list of whitelisted domains per users. Maybe this can work, I suggest you experiment with this.
core/Controller/CloudController.php
Outdated
*/ | ||
public function options() { | ||
// for cross-domain request checks | ||
header("Access-Control-Allow-Origin: *"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above
lib/private/legacy/response.php
Outdated
@@ -104,6 +104,10 @@ static public function setStatus($status) { | |||
break; | |||
} | |||
header($protocol.' '.$status); | |||
header("Access-Control-Allow-Origin: *"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see above
apps/provisioning_api/lib/Users.php
Outdated
header("Access-Control-Allow-Headers: authorization, OCS-APIREQUEST, Origin, X-Requested-With, Content-Type, Access-Control-Allow-Origin"); | ||
header("Access-Control-Allow-Methods: GET, OPTIONS, POST, PUT, DELETE, MKCOL, PROPFIND"); | ||
header("Access-Control-Allow-Credentials: true"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't use Access-Control-Allow-Origin: * together with Access-Control-Allow-Credentials: true. With this you can send cookies, headers and certificates from every other domain to the server. Chrome and firefox will block this with an error. You should use one or more domain names, coming from a whitelist, in Access-Control-Allow-Origin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true". This is the error
@PVince81 @Peter-Prochaska Please review. Steps For Testing:
<!DOCTYPE html>
<html>
<head>
<title>owncloud Lib. test</title>
<script type="text/javascript" src="./bundle.js"></script>
<script type="text/javascript">
// var oc is global
oc.setInstance('INSTANCE');
oc.login('USERNAME', 'PASSWORD').then(status => {
// alert(status);
return oc.users.getUsers();
}).then(users => {
console.log(users);
return oc.groups.groupExists('admin');
}).then(groups => {
console.log(groups);
return oc.apps.getApps();
}).then(apps => {
console.log(apps);
}).catch(error => {
alert(error);
});
</script>
</head>
<body>
Check the console.
</body>
</html>
|
@@ -40,6 +40,13 @@ | |||
\OC::$server->getLogger(), | |||
\OC::$server->getTwoFactorAuthManager() | |||
); | |||
API::register('options', '/cloud/users', [$users, 'options'], 'provisioning_api', API::GUEST_AUTH); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe better implement this directly in the OCS router ?
One idea would be to have a @CORS
annotation in every method that allow it which would tell the router to also automatically accept the OPTIONS method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah right... OCS might not be as flexible...
But the app framework supports annotations so it would work there.
I thought we already rewired OCS to App framework somewhere... needs some research
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a quick look at API::register()
, it seems we might not be able to use annotations easily as OCS is not the AppFramework unfortunately. One alternative would be to add an extra argument $options
to register()
and then register the routes this way:
API::register('post', '/cloud/users', [$users, 'options'], 'provisioning_api', API::GUEST_AUTH, ['cors' => true]);
but then multiple methods might be registered for the same routes and CORS doesn't restrict by verb.
So yet another approach, add a new method:
API::enableCors('/cloud/users');
This would enable CORS for the given route.
core/Controller/CloudController.php
Outdated
*/ | ||
public function options() { | ||
// for cross-domain request checks | ||
header("Access-Control-Allow-Origin: *"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this duplicate, again... yeah I think the annotation / route approach is way better so all this header logic would be in the router code instead
core/Controller/CloudController.php
Outdated
@@ -34,12 +34,40 @@ public function __construct($appName, IRequest $request) { | |||
} | |||
|
|||
/** | |||
* @PublicPage | |||
* @NoCSRFRequired |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, looks like annotations do work, hmm...
lib/private/legacy/response.php
Outdated
if (in_array($domain, $allowedDomains)) { | ||
header("Access-Control-Allow-Origin: " . $domain); | ||
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Access-Control-Allow-Origin"); | ||
header("Access-Control-Allow-Methods: GET, OPTIONS, POST, PUT, DELETE, MKCOL, PROPFIND"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this list could be (optionally) retrieved from all known registered routes.
So if only "GET" and "POST" were registered for that route, only allow these then.
Nice to have, not a requirement I think.
@noveens can you explain the CORS approach that you wanted to use ? I remember you said that you had to pick "*" for the first request / preflight and then restrict the domain for the second one ? Can you summarize the flow step by step ? |
My Approach: Q1. First, store domains for which the user wants to allow CORS for. (User savable, not admin) Q2. Pre-flight request(OPTIONS)? Q3. For the main request? @PVince81 @Peter-Prochaska Please Check the approach. |
apps/dav/appinfo/v1/webdav.php
Outdated
@@ -50,6 +50,23 @@ | |||
); | |||
$requestUri = \OC::$server->getRequest()->getRequestUri(); | |||
|
|||
if (\OC::$server->getRequest()->getMethod() === "OPTIONS") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be written as a Sabre plugin instead, if possible
apps/provisioning_api/lib/Apps.php
Outdated
* @param array $parameters | ||
* @return OC_OCS_Result | ||
*/ | ||
public function getApps($parameters) { | ||
$this->setCorsHeaders(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use @CORS
annotation instead ? or is it having trouble with OCS API ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The @CORS
notation doesn't work with OCS (Basically all those APIs whose routes are registered using the API::register
method).
ocs/v1.php
Outdated
@@ -81,6 +90,16 @@ | |||
OC::handleLogin(\OC::$server->getRequest()); | |||
} | |||
|
|||
if (\OC::$server->getRequest()->getMethod() === "OPTIONS") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any chance to put this inside the OCS router instead ?
@PVince81 @Peter-Prochaska , Please check this static web-app I built: |
@@ -118,6 +118,14 @@ public function afterController($controller, $methodName, Response $response){ | |||
if(isset($this->request->server['HTTP_ORIGIN']) && | |||
$this->reflector->hasAnnotation('CORS')) { | |||
|
|||
if (\OC::$server->getAppManager()->isEnabledForUser('cors')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably put the whitelisting thing directly into the core. I don't see any benefit of having this in a separate app.
Also having core depend on apps and hard-coded app names is usually not a good idea.
Can you bring the whitelist logic into this PR ? The likely right location is under settings/ somewhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the other alternative is bundling the "cors" app into core's "apps/cors" but I don't see any benefit of having this code into a separate app.
@DeepDiver1975 thoughts ? https://github.com/noveens/cors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @PVince81. Why should we have a separate app?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code looks good. But no need to have a separate app.
@@ -118,6 +118,14 @@ public function afterController($controller, $methodName, Response $response){ | |||
if(isset($this->request->server['HTTP_ORIGIN']) && | |||
$this->reflector->hasAnnotation('CORS')) { | |||
|
|||
if (\OC::$server->getAppManager()->isEnabledForUser('cors')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @PVince81. Why should we have a separate app?
@PVince81 @Peter-Prochaska, Thanks for your approvals, I just have one doubt now: So I am worried that whether the previous implementation of |
Not sure but since @Peter-Prochaska said that the wildcard domain is not acceptable, it is likely that the way how the old @Peter-Prochaska can you confirm ? |
lib/private/legacy/response.php
Outdated
* This function adds the CORS headers if the requester domain is white-listed | ||
*/ | ||
public static function setCorsHeaders($userId, $domain) { | ||
$allowedDomains = explode(",", \OC::$server->getConfig()->getUserValue($userId, 'cors', 'domains')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest renaming "cors" to "core" and the key to "cors_domains" as there is no more "cors" app.
Please check if other locations in code require a similar change.
Yes, i can confirm |
@noveens please fix//enhance the unit tests |
Looks like the HTTP_ORIGIN header is not guaranteed to be always there ?
from the Travis failures. @noveens Maybe add a |
backport for 10.0.4, I'd like @Peter-Prochaska to have a second look |
@noveens please backport to stable10 (PR for stable10 with the same commit) |
Note: needs #28833 backported on top of the backport of this. |
@jesmrec I think this one might be the one behind the behavior we saw today with OAuth app; i.e. getting no-content on the EDIT: Error seems to be located in (the lack of)
(see #28860 (comment)) & likely to break on other |
thanks @SamuAlfageme. I will check. The point is mobile apps that are using OAuth2 as auth method are broken with the owncloud daily master from today (30th Aug). It is caused at least for those request with "format=json" as parameter, that are being returned an empty body from the server side. I will send more info when i confirm. CC @PVince81 |
@SamuAlfageme @jesmrec any detailed steps to reproduce ? Does the oauth app use the OPTIONS method ? |
@SamuAlfageme @jesmrec once the problem identified, if any fixing needed, please raise a new ticket and ping @noveens |
Problem was fixed in #28864, good job! |
[stable10] Backport of #28457 (Enabled CORS on ownCloud)
Why is this option shown to end users if an Admin only has permissions to edit? |
No, this option is editable by users only, not the admin. At some point there should be a new page where the admin can see the CORS domains set up by all users. |
@PVince81 : Well in 10.0.7 if a user attempts, they are shown this error: |
Ouch, then it's a bug... |
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Description
Manually added the * header
Motivation and Context
Trying to enable CORS.
Testing instructions:
@PVince81