diff --git a/apps/dav/lib/Connector/Sabre/CorsPlugin.php b/apps/dav/lib/Connector/Sabre/CorsPlugin.php
new file mode 100644
index 00000000000..718403e356a
--- /dev/null
+++ b/apps/dav/lib/Connector/Sabre/CorsPlugin.php
@@ -0,0 +1,189 @@
+
+ *
+ * @copyright Copyright (c) 2018, ownCloud GmbH
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see
+ *
+ */
+
+namespace OCA\DAV\Connector\Sabre;
+
+use OCP\IUserSession;
+use OCP\Util;
+use Sabre\DAV\ServerPlugin;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+/**
+ * Class CorsPlugin is a plugin which adds CORS headers to the responses
+ */
+class CorsPlugin extends ServerPlugin {
+ /**
+ * Reference to main server object
+ *
+ * @var \Sabre\DAV\Server
+ */
+ private $server;
+
+ /**
+ * Reference to logged in user's session
+ *
+ * @var IUserSession
+ */
+ private $userSession;
+
+ /** @var array */
+ private $extraHeaders;
+ /**
+ * @var bool
+ */
+ private $alreadyExecuted = false;
+
+ /**
+ * @param IUserSession $userSession
+ */
+ public function __construct(IUserSession $userSession) {
+ $this->userSession = $userSession;
+ }
+
+ private function getExtraHeaders(RequestInterface $request) {
+ if ($this->extraHeaders === null) {
+ if ($this->userSession->getUser() === null) {
+ $this->extraHeaders['Access-Control-Allow-Methods'] = [
+ 'OPTIONS',
+ 'GET',
+ 'HEAD',
+ 'DELETE',
+ 'PROPFIND',
+ 'PUT',
+ 'PROPPATCH',
+ 'COPY',
+ 'MOVE',
+ 'REPORT',
+ 'SEARCH',
+ ];
+ } else {
+ $this->extraHeaders['Access-Control-Allow-Methods'] = $this->server->getAllowedMethods($request->getPath());
+ }
+ }
+ return $this->extraHeaders;
+ }
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by \Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param \Sabre\DAV\Server $server
+ * @return void
+ */
+ public function initialize(\Sabre\DAV\Server $server) {
+ $this->server = $server;
+
+ $request = $this->server->httpRequest;
+ if (!$request->hasHeader('Origin')) {
+ return;
+ }
+ $originHeader = $request->getHeader('Origin');
+ if ($this->ignoreOriginHeader($originHeader)) {
+ return;
+ }
+ if (Util::isSameDomain($originHeader, $request->getAbsoluteUrl())) {
+ return;
+ }
+
+ $this->server->on('beforeMethod:*', [$this, 'setCorsHeaders']);
+ $this->server->on('exception', [$this, 'onException']);
+ $this->server->on('beforeMethod:OPTIONS', [$this, 'setOptionsRequestHeaders'], 5);
+ }
+
+ public function onException(\Throwable $ex) {
+ $this->setCorsHeaders($this->server->httpRequest, $this->server->httpResponse);
+ }
+
+ /**
+ * This method sets the cors headers for all requests
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ public function setCorsHeaders(RequestInterface $request, ResponseInterface $response) {
+ if ($request->getHeader('origin') === null) {
+ return;
+ }
+ if ($this->alreadyExecuted) {
+ return;
+ }
+ $this->alreadyExecuted = true;
+ $requesterDomain = $request->getHeader('origin');
+ // unauthenticated request shall add cors headers as well
+ $userId = null;
+ if ($this->userSession->getUser() !== null) {
+ $userId = $this->userSession->getUser()->getUID();
+ }
+
+ $headers = \OC_Response::setCorsHeaders($userId, $requesterDomain, null, $this->getExtraHeaders($request));
+ foreach ($headers as $key => $value) {
+ $response->addHeader($key, \implode(',', $value));
+ }
+ }
+
+ /**
+ * Handles the OPTIONS request
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ *
+ * @return false|void
+ * @throws \InvalidArgumentException
+ */
+ public function setOptionsRequestHeaders(RequestInterface $request, ResponseInterface $response) {
+ $authorization = $request->getHeader('Authorization');
+ if ($authorization === null || $authorization === '') {
+ // Set the proper response
+ $response->setStatus(200);
+ $response = \OC_Response::setOptionsRequestHeaders($response, $this->getExtraHeaders($request));
+
+ // Since All OPTIONS requests are unauthorized, we will have to return false from here
+ // If we don't return false, due to no authorization, a 401-Unauthorized will be thrown
+ // Which we don't want here
+ // Hence this sendResponse
+ $this->server->sapi->sendResponse($response);
+ return false;
+ }
+ }
+
+ /**
+ * in addition to schemas used by extensions we ignore empty origin header
+ * values as well as 'null' which is not valid by the specification but used
+ * by some clients.
+ * @link https://github.com/owncloud/core/pull/32120#issuecomment-407008243
+ *
+ * @param string $originHeader
+ * @return bool
+ */
+ public function ignoreOriginHeader($originHeader) {
+ if (\in_array($originHeader, ['', null, 'null'], true)) {
+ return true;
+ }
+ $schema = \parse_url($originHeader, PHP_URL_SCHEME);
+ return \in_array(\strtolower($schema), ['moz-extension', 'chrome-extension']);
+ }
+}
diff --git a/apps/dav/lib/Connector/Sabre/ServerFactory.php b/apps/dav/lib/Connector/Sabre/ServerFactory.php
index d0cc8aab5d0..eb6dfe8317e 100644
--- a/apps/dav/lib/Connector/Sabre/ServerFactory.php
+++ b/apps/dav/lib/Connector/Sabre/ServerFactory.php
@@ -100,6 +100,7 @@ class ServerFactory {
// Load plugins
$server->addPlugin(new \OCA\DAV\Connector\Sabre\MaintenancePlugin($this->config, $this->l10n));
+ $server->addPlugin(new \OCA\DAV\Connector\Sabre\CorsPlugin($this->userSession));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin($this->config));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\AnonymousOptionsPlugin());
$server->addPlugin($authPlugin);
diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php
index 603e015fca9..84e55d6247c 100644
--- a/apps/dav/lib/Server.php
+++ b/apps/dav/lib/Server.php
@@ -51,6 +51,7 @@ use OCA\DAV\Connector\Sabre\CachingTree;
use OCA\DAV\Connector\Sabre\ChecksumUpdatePlugin;
use OCA\DAV\Connector\Sabre\CommentPropertiesPlugin;
use OCA\DAV\Connector\Sabre\CopyEtagHeaderPlugin;
+use OCA\DAV\Connector\Sabre\CorsPlugin;
use OCA\DAV\Connector\Sabre\DavAclPlugin;
use OCA\DAV\Connector\Sabre\DummyGetResponsePlugin;
use OCA\DAV\Connector\Sabre\FakeLockerPlugin;
@@ -130,6 +131,8 @@ class Server {
$this->server->addPlugin(new ProfilerPlugin($this->request));
$this->server->addPlugin(new BlockLegacyClientPlugin(\OC::$server->getConfig()));
$this->server->addPlugin(new AnonymousOptionsPlugin());
+ $this->server->addPlugin(new CorsPlugin(\OC::$server->getUserSession()));
+
$authPlugin = new Plugin();
$authPlugin->addBackend(new PublicAuth());
$this->server->addPlugin($authPlugin);
diff --git a/apps/dav/tests/unit/Connector/Sabre/CorsPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/CorsPluginTest.php
new file mode 100644
index 00000000000..48daf4fb819
--- /dev/null
+++ b/apps/dav/tests/unit/Connector/Sabre/CorsPluginTest.php
@@ -0,0 +1,375 @@
+
+ *
+ * @copyright Copyright (c) 2018, ownCloud GmbH
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see
+ *
+ */
+namespace OCA\DAV\Tests\unit\Connector\Sabre;
+
+use OCA\DAV\Connector\Sabre\CorsPlugin;
+use OCP\IUserSession;
+use OCP\IUser;
+use OCP\IConfig;
+use Sabre\DAV\Server;
+use Sabre\DAV\ServerPlugin;
+use Test\TestCase;
+
+class CorsPluginTest extends TestCase {
+ /**
+ * @var Server
+ */
+ private $server;
+
+ /**
+ * @var CorsPlugin
+ */
+ private $plugin;
+
+ /**
+ * @var IUserSession | \PHPUnit\Framework\MockObject\MockObject
+ */
+ private $userSession;
+
+ /**
+ * @var IConfig | \PHPUnit\Framework\MockObject\MockObject
+ */
+ private $config;
+
+ public function setUp(): void {
+ parent::setUp();
+ $this->server = new Server();
+
+ $this->server->sapi = $this->getMockBuilder(\stdClass::class)
+ ->setMethods(['sendResponse'])
+ ->getMock();
+
+ $this->server->httpRequest->setMethod('OPTIONS');
+
+ $this->userSession = $this->createMock(IUserSession::class);
+
+ $this->config = $this->createMock(IConfig::class);
+ $this->overwriteService('AllConfig', $this->config);
+
+ $this->plugin = new CorsPlugin($this->userSession);
+
+ /** @var ServerPlugin | \PHPUnit\Framework\MockObject\MockObject $extraMethodPlugin */
+ $extraMethodPlugin = $this->createMock(ServerPlugin::class);
+ $extraMethodPlugin->method('getHTTPMethods')
+ ->with('owncloud/remote.php/dav/files/user1/target/path')
+ ->willReturn(['EXTRA']);
+ $extraMethodPlugin->method('getFeatures')->willReturn([]);
+
+ $this->server->addPlugin($extraMethodPlugin);
+ }
+
+ public function tearDown(): void {
+ $this->restoreService('AllConfig');
+ }
+
+ public function optionsCases() {
+ $allowedDomains = '["https://requesterdomain.tld", "http://anotherdomain.tld"]';
+
+ $allowedHeaders = [
+ 'OC-Checksum', 'OC-Total-Length', 'OCS-APIREQUEST', 'X-OC-Mtime',
+ 'OC-RequestAppPassword', 'Accept',
+ 'Authorization', 'Brief', 'Content-Length', 'Content-Range',
+ 'Content-Type', 'Date', 'Depth', 'Destination', 'Host', 'If', 'If-Match',
+ 'If-Modified-Since', 'If-None-Match', 'If-Range', 'If-Unmodified-Since',
+ 'Location', 'Lock-Token', 'Overwrite', 'Prefer', 'Range', 'Schedule-Reply',
+ 'Timeout', 'User-Agent', 'X-Expected-Entity-Length', 'Accept-Language',
+ 'Access-Control-Request-Method', 'Access-Control-Allow-Origin', 'Cache-Control', 'ETag',
+ 'OC-Autorename', 'OC-CalDav-Import', 'OC-Chunked', 'OC-Etag', 'OC-FileId',
+ 'OC-LazyOps', 'OC-Total-File-Length', 'Origin', 'X-Request-ID', 'X-Requested-With'
+ ];
+ $allowedMethods = [
+ 'GET',
+ 'OPTIONS',
+ 'POST',
+ 'PUT',
+ 'DELETE',
+ 'MKCOL',
+ 'PROPFIND',
+ 'PATCH',
+ 'PROPPATCH',
+ 'REPORT',
+ 'HEAD',
+ 'COPY',
+ 'MOVE',
+ 'EXTRA',
+ ];
+ $allowedMethodsUnAuthenticated = [
+ 'GET',
+ 'OPTIONS',
+ 'POST',
+ 'PUT',
+ 'DELETE',
+ 'MKCOL',
+ 'PROPFIND',
+ 'PATCH',
+ 'PROPPATCH',
+ 'REPORT',
+ 'HEAD',
+ 'COPY',
+ 'MOVE',
+ ];
+
+ return [
+ 'OPTIONS headers' =>
+ [
+ $allowedDomains,
+ false,
+ [
+ 'Origin' => 'https://requesterdomain.tld',
+ ],
+ 200,
+ [
+ 'Access-Control-Allow-Headers' => \implode(',', $allowedHeaders),
+ 'Access-Control-Allow-Origin' => '*',
+ 'Access-Control-Allow-Methods' => \implode(',', $allowedMethodsUnAuthenticated),
+ ],
+ false
+ ],
+ 'OPTIONS headers with user' =>
+ [
+ $allowedDomains,
+ true,
+ [
+ 'Origin' => 'https://requesterdomain.tld',
+ 'Authorization' => 'abc',
+ ],
+ 200,
+ [
+ 'Access-Control-Allow-Headers' => \implode(',', $allowedHeaders),
+ 'Access-Control-Allow-Origin' => 'https://requesterdomain.tld',
+ 'Access-Control-Allow-Methods' => \implode(',', $allowedMethods),
+ ],
+ true
+ ],
+ 'OPTIONS headers no user' =>
+ [
+ $allowedDomains,
+ false,
+ [
+ 'Origin' => 'https://requesterdomain.tld',
+ 'Authorization' => 'abc',
+ ],
+ 200,
+ [
+ 'Access-Control-Allow-Headers' => null,
+ 'Access-Control-Allow-Origin' => null,
+ 'Access-Control-Allow-Methods' => null,
+ ],
+ true
+ ],
+ 'OPTIONS headers domain not allowed' =>
+ [
+ '[]',
+ true,
+ [
+ 'Origin' => 'https://requesterdomain.tld',
+ 'Authorization' => 'abc',
+ ],
+ 200,
+ [
+ 'Access-Control-Allow-Headers' => null,
+ 'Access-Control-Allow-Origin' => null,
+ 'Access-Control-Allow-Methods' => null,
+ ],
+ true
+ ],
+ 'OPTIONS headers not allowed but no cross-domain' =>
+ [
+ '[]',
+ true,
+ [
+ 'Origin' => 'https://requesterdomain.tld',
+ 'Authorization' => 'abc',
+ ],
+ 200,
+ [
+ 'Access-Control-Allow-Headers' => null,
+ 'Access-Control-Allow-Origin' => null,
+ 'Access-Control-Allow-Methods' => null,
+ ],
+ true
+ ],
+ 'OPTIONS headers allowed but no cross-domain' =>
+ [
+ '["currentdomain.tld:8080"]',
+ true,
+ [
+ 'Origin' => 'https://currentdomain.tld:8080',
+ 'Authorization' => 'abc',
+ ],
+ 200,
+ [
+ 'Access-Control-Allow-Headers' => null,
+ 'Access-Control-Allow-Origin' => null,
+ 'Access-Control-Allow-Methods' => null,
+ ],
+ true
+ ],
+ 'OPTIONS headers allowed, cross-domain through different port' =>
+ [
+ '["https://currentdomain.tld:8443"]',
+ true,
+ [
+ 'Origin' => 'https://currentdomain.tld:8443',
+ 'Authorization' => 'abc',
+ ],
+ 200,
+ [
+ 'Access-Control-Allow-Headers' => \implode(',', $allowedHeaders),
+ 'Access-Control-Allow-Origin' => 'https://currentdomain.tld:8443',
+ 'Access-Control-Allow-Methods' => \implode(',', $allowedMethods),
+ ],
+ true
+ ],
+ 'no Origin header' =>
+ [
+ $allowedDomains,
+ true,
+ [
+ ],
+ 200,
+ [
+ 'Access-Control-Allow-Headers' => null,
+ 'Access-Control-Allow-Origin' => null,
+ 'Access-Control-Allow-Methods' => null,
+ ],
+ true
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider optionsCases
+ * @param $allowedDomains
+ * @param $hasUser
+ * @param $requestHeaders
+ * @param $expectedStatus
+ * @param array $expectedHeaders
+ * @param bool $expectDavHeaders
+ */
+ public function testOptionsHeaders($allowedDomains, $hasUser, $requestHeaders, $expectedStatus, array $expectedHeaders, $expectDavHeaders = false) {
+ $this->server->sapi->expects($this->once())->method('sendResponse')->with($this->server->httpResponse);
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('someuser');
+
+ if ($hasUser) {
+ $this->userSession->method('getUser')->willReturn($user);
+ } else {
+ $this->userSession->method('getUser')->willReturn(null);
+ }
+
+ $this->config->method('getSystemValue')->willReturn([]);
+ $this->config->method('getUserValue')
+ ->with('someuser', 'core', 'domains')
+ ->willReturn($allowedDomains);
+
+ $this->server->httpRequest->setHeaders($requestHeaders);
+ $this->server->httpRequest->setAbsoluteUrl('https://currentdomain.tld:8080/owncloud/remote.php/dav/files/user1/target/path');
+ $this->server->httpRequest->setUrl('/owncloud/remote.php/dav/files/user1/target/path');
+
+ $this->server->addPlugin($this->plugin);
+ $this->server->start();
+
+ $this->assertEquals($expectedStatus, $this->server->httpResponse->getStatus());
+
+ foreach ($expectedHeaders as $headerKey => $headerValue) {
+ if ($headerValue !== null) {
+ $this->assertTrue($this->server->httpResponse->hasHeader($headerKey), "Response header \"$headerKey\" exists");
+ } else {
+ $this->assertFalse($this->server->httpResponse->hasHeader($headerKey), "Response header \"$headerKey\" does not exist");
+ }
+ $this->assertEquals($headerValue, $this->server->httpResponse->getHeader($headerKey));
+ }
+
+ // if it has DAV headers, it means we did not bypass further processing
+ $this->assertEquals($expectDavHeaders, $this->server->httpResponse->hasHeader('DAV'));
+ }
+
+ /**
+ * @dataProvider providesOriginUrls
+ * @param $expectedValue
+ * @param $url
+ */
+ public function testExtensionRequests($expectedValue, $url) {
+ $plugin = new CorsPlugin($this->createMock(IUserSession::class));
+ self::assertEquals($expectedValue, $plugin->ignoreOriginHeader($url));
+ }
+
+ public function providesOriginUrls() {
+ return [
+ 'Firefox extension' => [true, 'moz-extension://mgmnhfbjphngabcpbpmapnnaabhnchmi/'],
+ 'Chrome extension' => [true, 'chrome-extension://mgmnhfbjphngabcpbpmapnnaabhnchmi/'],
+ 'Empty Origin' => [true, ''],
+ 'Null string Origin' => [true, 'null'],
+ 'Null Origin' => [true, null],
+ 'plain http' => [false, 'http://example.net/'],
+ ];
+ }
+
+ public function testAuthenticatedAdditionalAllowedHeaders() {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('someuser');
+
+ $this->userSession->method('getUser')->willReturn($user);
+ $this->server->httpRequest->setHeader('Origin', 'https://requesterdomain.tld');
+ $this->server->httpRequest->setUrl('/owncloud/remote.php/dav/files/user1/target/path');
+
+ $this->config->method('getSystemValue')->withConsecutive(
+ ['cors.allowed-domains', []],
+ ['cors.allowed-headers', []]
+ )
+ ->willReturnMap([
+ ['cors.allowed-domains', [], []],
+ ['cors.allowed-headers', [], ['X-Additional-Configured-Header', 'authorization']]
+ ]);
+ $this->config->method('getUserValue')->willReturn('["https://requesterdomain.tld"]');
+
+ $this->server->addPlugin($this->plugin);
+
+ $this->plugin->setCorsHeaders($this->server->httpRequest, $this->server->httpResponse);
+ self::assertEquals(
+ 'X-Additional-Configured-Header,authorization,OC-Checksum,OC-Total-Length,OCS-APIREQUEST,X-OC-Mtime,OC-RequestAppPassword,Accept,Authorization,Brief,Content-Length,Content-Range,Content-Type,Date,Depth,Destination,Host,If,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Location,Lock-Token,Overwrite,Prefer,Range,Schedule-Reply,Timeout,User-Agent,X-Expected-Entity-Length,Accept-Language,Access-Control-Request-Method,Access-Control-Allow-Origin,Cache-Control,ETag,OC-Autorename,OC-CalDav-Import,OC-Chunked,OC-Etag,OC-FileId,OC-LazyOps,OC-Total-File-Length,Origin,X-Request-ID,X-Requested-With',
+ $this->server->httpResponse->getHeader('Access-Control-Allow-Headers')
+ );
+ }
+
+ public function testUnauthenticatedAdditionalAllowedHeaders() {
+ $this->userSession->method('getUser')->willReturn(null);
+ $this->server->httpRequest->setHeader('Origin', 'https://requesterdomain.tld');
+
+ $this->config->method('getSystemValue')->withConsecutive(
+ ['cors.allowed-domains', []],
+ ['cors.allowed-headers', []]
+ )
+ ->willReturnMap([
+ ['cors.allowed-domains', [], ['https://requesterdomain.tld']],
+ ['cors.allowed-headers', [], ['X-Additional-Configured-Header', 'authorization']]
+ ]);
+
+ $this->server->addPlugin($this->plugin);
+
+ $this->plugin->setCorsHeaders($this->server->httpRequest, $this->server->httpResponse);
+ self::assertEquals(
+ 'X-Additional-Configured-Header,authorization,OC-Checksum,OC-Total-Length,OCS-APIREQUEST,X-OC-Mtime,OC-RequestAppPassword,Accept,Authorization,Brief,Content-Length,Content-Range,Content-Type,Date,Depth,Destination,Host,If,If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,Location,Lock-Token,Overwrite,Prefer,Range,Schedule-Reply,Timeout,User-Agent,X-Expected-Entity-Length,Accept-Language,Access-Control-Request-Method,Access-Control-Allow-Origin,Cache-Control,ETag,OC-Autorename,OC-CalDav-Import,OC-Chunked,OC-Etag,OC-FileId,OC-LazyOps,OC-Total-File-Length,Origin,X-Request-ID,X-Requested-With',
+ $this->server->httpResponse->getHeader('Access-Control-Allow-Headers')
+ );
+ }
+}
diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php
index a012d1e8ea6..bc20037e81d 100644
--- a/lib/private/AppFramework/DependencyInjection/DIContainer.php
+++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php
@@ -234,7 +234,8 @@ class DIContainer extends SimpleContainer implements IAppContainer {
$c->get(IRequest::class),
$c->get(IControllerMethodReflector::class),
$c->get(IUserSession::class),
- $c->get(IThrottler::class)
+ $c->get(IThrottler::class),
+ $c->get(IConfig::class),
)
);
$dispatcher->registerMiddleware(
diff --git a/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php b/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
index 8bdacf550b6..c3f1a105400 100644
--- a/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
@@ -29,7 +29,6 @@ namespace OC\AppFramework\Middleware\Security;
use OC\AppFramework\Middleware\Security\Exceptions\SecurityException;
use OC\AppFramework\Utility\ControllerMethodReflector;
use OC\Authentication\Exceptions\PasswordLoginForbiddenException;
-use OC\User\Session;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
@@ -37,7 +36,9 @@ use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Middleware;
+use OCP\IConfig;
use OCP\IRequest;
+use OCP\IUserSession;
use OCP\Security\Bruteforce\IThrottler;
use ReflectionMethod;
@@ -48,23 +49,12 @@ use ReflectionMethod;
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
*/
class CORSMiddleware extends Middleware {
- /** @var IRequest */
- private $request;
- /** @var ControllerMethodReflector */
- private $reflector;
- /** @var Session */
- private $session;
- /** @var IThrottler */
- private $throttler;
-
- public function __construct(IRequest $request,
- ControllerMethodReflector $reflector,
- Session $session,
- IThrottler $throttler) {
- $this->request = $request;
- $this->reflector = $reflector;
- $this->session = $session;
- $this->throttler = $throttler;
+ public function __construct(private IRequest $request,
+ private ControllerMethodReflector $reflector,
+ private IUserSession $session,
+ private IThrottler $throttler,
+ private IConfig $config,
+ ) {
}
/**
@@ -135,24 +125,25 @@ class CORSMiddleware extends Middleware {
* @throws SecurityException
*/
public function afterController($controller, $methodName, Response $response) {
+ $userId = !is_null($this->session->getUser()) ? $this->session->getUser()->getUID() : null;
+
// only react if it's a CORS request and if the request sends origin and
+ $reflectionMethod = new ReflectionMethod($controller, $methodName);
+ if ($this->request->getHeader("Origin") !== null
+ && $this->hasAnnotationOrAttribute($reflectionMethod, 'CORS', CORS::class)
+ && !is_null($userId)) {
+ $requesterDomain = $this->request->getHeader("Origin");
+ \OC_Response::setCorsHeaders($userId, $requesterDomain, $this->config);
- if (isset($this->request->server['HTTP_ORIGIN'])) {
- $reflectionMethod = new ReflectionMethod($controller, $methodName);
- if ($this->hasAnnotationOrAttribute($reflectionMethod, 'CORS', CORS::class)) {
- // allow credentials headers must not be true or CSRF is possible
- // otherwise
- foreach ($response->getHeaders() as $header => $value) {
- if (strtolower($header) === 'access-control-allow-credentials' &&
- strtolower(trim($value)) === 'true') {
- $msg = 'Access-Control-Allow-Credentials must not be '.
- 'set to true in order to prevent CSRF';
- throw new SecurityException($msg);
- }
+ // allow credentials headers must not be true or CSRF is possible
+ // otherwise
+ foreach ($response->getHeaders() as $header => $value) {
+ if (strtolower($header) === 'access-control-allow-credentials' &&
+ strtolower(trim($value)) === 'true') {
+ $msg = 'Access-Control-Allow-Credentials must not be '.
+ 'set to true in order to prevent CSRF';
+ throw new SecurityException($msg);
}
-
- $origin = $this->request->server['HTTP_ORIGIN'];
- $response->addHeader('Access-Control-Allow-Origin', $origin);
}
}
return $response;
diff --git a/lib/private/Route/Router.php b/lib/private/Route/Router.php
index fe97623176d..ac08a56dfff 100644
--- a/lib/private/Route/Router.php
+++ b/lib/private/Route/Router.php
@@ -274,6 +274,41 @@ class Router implements IRouter {
}
$matcher = new UrlMatcher($this->root, $this->context);
+
+ if (\OC::$server->getRequest()->getMethod() === "OPTIONS") {
+ try {
+ // Checking whether the actual request (one which OPTIONS is pre-flight for)
+ // Is actually valid
+ $requestingMethod = \OC::$server->getRequest()->getHeader('Access-Control-Request-Method');
+ $tempContext = $this->context;
+ $tempContext->setMethod($requestingMethod);
+ $tempMatcher = new UrlMatcher($this->root, $tempContext);
+ $parameters = $tempMatcher->match($url);
+
+ // Reach here if it's valid
+ $response = new \OC\OCS\Result(null, 100, 'OPTIONS request successful');
+ $response = \OC_Response::setOptionsRequestHeaders($response);
+ \OC_API::respond($response, \OC_API::requestedFormat());
+
+ // Return since no more processing for an OPTIONS request is required
+ return;
+ } catch (ResourceNotFoundException $e) {
+ if (substr($url, -1) !== '/') {
+ // We allow links to apps/files? for backwards compatibility reasons
+ // However, since Symfony does not allow empty route names, the route
+ // we need to match is '/', so we need to append the '/' here.
+ try {
+ $parameters = $matcher->match($url . '/');
+ } catch (ResourceNotFoundException $newException) {
+ // If we still didn't match a route, we throw the original exception
+ throw $e;
+ }
+ } else {
+ throw $e;
+ }
+ }
+ }
+
try {
$parameters = $matcher->match($url);
} catch (ResourceNotFoundException $e) {
diff --git a/lib/private/legacy/OC_Response.php b/lib/private/legacy/OC_Response.php
index 9440feae3cd..a4646799c62 100644
--- a/lib/private/legacy/OC_Response.php
+++ b/lib/private/legacy/OC_Response.php
@@ -103,4 +103,200 @@ class OC_Response {
header('X-XSS-Protection: 1; mode=block'); // Enforce browser based XSS filters
}
}
+
+ /**
+ * This function adds the CORS headers if the requester domain is white-listed
+ *
+ * @param string $userId
+ * @param string $domain
+ * @param \OCP\IConfig $config
+ * @param array $headers
+ *
+ * Format of $headers:
+ * Array [
+ * "Access-Control-Allow-Headers": ["a", "b", "c"],
+ * "Access-Control-Allow-Origin": ["a", "b", "c"],
+ * "Access-Control-Allow-Methods": ["a", "b", "c"]
+ * ]
+ *
+ * @return array
+ */
+ public static function setCorsHeaders($userId, $domain, \OCP\IConfig $config = null, array $headers = []) {
+ if ($config === null) {
+ $config = \OC::$server->getConfig();
+ }
+ // first check if any of the global CORS domains matches
+ $globalAllowedDomains = $config->getSystemValue('cors.allowed-domains', []);
+ $isCorsRequest = (\is_array($globalAllowedDomains) && \in_array($domain, $globalAllowedDomains, true));
+ if (!$isCorsRequest && $userId !== null) {
+ // check if any of the user specific CORS domains matches
+ $allowedDomains = \json_decode($config->getUserValue($userId, 'core', 'domains'), true);
+ $isCorsRequest = (\is_array($allowedDomains) && \in_array($domain, $allowedDomains, true));
+ }
+ if ($isCorsRequest) {
+ // TODO: infer allowed verbs from existing known routes
+ $allHeaders['Access-Control-Allow-Headers'] = self::getAllowedCorsHeaders($config);
+ $allHeaders['Access-Control-Expose-Headers'] = self::getExposeCorsHeaders();
+ $allHeaders['Access-Control-Allow-Origin'] = [$domain];
+ $allHeaders['Access-Control-Allow-Methods'] = ['GET', 'OPTIONS', 'POST', 'PUT', 'DELETE', 'MKCOL', 'PROPFIND', 'PATCH', 'PROPPATCH', 'REPORT'];
+
+ foreach ($headers as $key => $value) {
+ if (\array_key_exists($key, $allHeaders)) {
+ $allHeaders[$key] = \array_unique(\array_merge($allHeaders[$key], $value));
+ }
+ }
+
+ return $allHeaders;
+ }
+ return [];
+ }
+
+ /**
+ * This function adds the CORS headers for all domains
+ *
+ * @param Sabre\HTTP\ResponseInterface $response
+ * @param array $headers
+ *
+ * Format of $headers:
+ * Array [
+ * "Access-Control-Allow-Headers": ["a", "b", "c"],
+ * "Access-Control-Allow-Origin": ["a", "b", "c"],
+ * "Access-Control-Allow-Methods": ["a", "b", "c"]
+ * ]
+ *
+ * @param \OCP\IConfig|null $config
+ * @return Sabre\HTTP\ResponseInterface $response
+ */
+ public static function setOptionsRequestHeaders($response, $headers = [], \OCP\IConfig $config = null) {
+ // TODO: infer allowed verbs from existing known routes
+ $allHeaders['Access-Control-Allow-Headers'] = self::getAllowedCorsHeaders($config);
+ $allHeaders['Access-Control-Allow-Origin'] = ['*'];
+ $allHeaders['Access-Control-Allow-Methods'] = self::getAllowedCorsMethods();
+
+ foreach ($headers as $key => $value) {
+ if (\array_key_exists($key, $allHeaders)) {
+ $allHeaders[$key] = \array_unique(\array_merge($allHeaders[$key], $value));
+ }
+ }
+
+ foreach ($allHeaders as $key => $value) {
+ $response->addHeader($key, \implode(',', $value));
+ }
+
+ return $response;
+ }
+
+ /**
+ * This are the allowed methods a browser can use from javascript code.
+ *
+ * @return string[]
+ */
+ private static function getAllowedCorsMethods() {
+ return [
+ 'GET',
+ 'OPTIONS',
+ 'POST',
+ 'PUT',
+ 'DELETE',
+ 'MKCOL',
+ 'PROPFIND',
+ 'PATCH',
+ 'PROPPATCH',
+ 'REPORT',
+ 'SEARCH'
+ ];
+ }
+
+ /**
+ * These are the header which a browser can access from javascript code.
+ * Simple headers are always accessible.
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers
+ *
+ * @return array
+ */
+ private static function getExposeCorsHeaders() {
+ return [
+ 'Content-Location',
+ 'DAV',
+ 'ETag',
+ 'Link',
+ 'Lock-Token',
+ 'OC-ETag',
+ 'OC-Checksum',
+ 'OC-FileId',
+ 'OC-JobStatus-Location',
+ 'OC-RequestAppPassword',
+ 'Vary',
+ 'Webdav-Location',
+ 'X-Sabre-Status',
+ ];
+ }
+
+ /**
+ * These are the headers the browser is allowed to ask for in a CORS request.
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
+ *
+ * @param \OCP\IConfig $config
+ * @return array|mixed
+ */
+ private static function getAllowedCorsHeaders(\OCP\IConfig $config = null) {
+ if ($config === null) {
+ $config = \OC::$server->getConfig();
+ }
+ $allowedDefaultHeaders = [
+ // own headers
+ 'OC-Checksum',
+ 'OC-Total-Length',
+ 'OCS-APIREQUEST',
+ 'X-OC-Mtime',
+ 'OC-RequestAppPassword',
+ // as used in sabre
+ 'Accept',
+ 'Authorization',
+ 'Brief',
+ 'Content-Length',
+ 'Content-Range',
+ 'Content-Type',
+ 'Date',
+ 'Depth',
+ 'Destination',
+ 'Host',
+ 'If',
+ 'If-Match',
+ 'If-Modified-Since',
+ 'If-None-Match',
+ 'If-Range',
+ 'If-Unmodified-Since',
+ 'Location',
+ 'Lock-Token',
+ 'Overwrite',
+ 'Prefer',
+ 'Range',
+ 'Schedule-Reply',
+ 'Timeout',
+ 'User-Agent',
+ 'X-Expected-Entity-Length',
+ // generally used headers in core
+ 'Accept-Language',
+ 'Access-Control-Request-Method',
+ 'Access-Control-Allow-Origin',
+ 'Cache-Control',
+ 'ETag',
+ 'OC-Autorename',
+ 'OC-CalDav-Import',
+ 'OC-Chunked',
+ 'OC-Etag',
+ 'OC-FileId',
+ 'OC-LazyOps',
+ 'OC-Total-File-Length',
+ 'OC-Total-Length',
+ 'Origin',
+ 'X-Request-ID',
+ 'X-Requested-With'
+ ];
+ $corsAllowedHeaders = $config->getSystemValue('cors.allowed-headers', []);
+ $corsAllowedHeaders = \array_merge($corsAllowedHeaders, $allowedDefaultHeaders);
+ $corsAllowedHeaders = \array_unique(\array_values($corsAllowedHeaders));
+ return $corsAllowedHeaders;
+ }
}
diff --git a/lib/public/Util.php b/lib/public/Util.php
index 781aa87d66b..8811db185dd 100644
--- a/lib/public/Util.php
+++ b/lib/public/Util.php
@@ -624,4 +624,59 @@ class Util {
}
return true;
}
+
+ /**
+ * Returns the protocol, domain and port as string in a normalized
+ * format for easier comparison.
+ * Example: "HTTPS://HOST.tld:8080" is returned as "https://host.tld:8080"
+ *
+ * If no port was specified, it will add the default port
+ * of the specified protocol (80 for http or 443 for https)
+ *
+ * @param string $url full url
+ * @return string protocol, domain and port as string
+ * @since 28.0.0
+ */
+ public static function getFullDomain(string $url): string {
+ $parts = \parse_url($url);
+ if ($parts === false) {
+ throw new \InvalidArgumentException('Invalid url "' . $url . '"');
+ }
+ if (!isset($parts['scheme']) || !isset($parts['host'])) {
+ throw new \InvalidArgumentException('Invalid url "' . $url . '"');
+ }
+ $protocol = \strtolower($parts['scheme']);
+ $host = \strtolower($parts['host']);
+ $port = null;
+ if ($protocol === 'http') {
+ $port = 80;
+ } elseif ($protocol === 'https') {
+ $port = 443;
+ } else {
+ throw new \InvalidArgumentException('Only http based URLs supported');
+ }
+
+ if (isset($parts['port']) && $port !== '') {
+ $port = $parts['port'];
+ }
+
+ return $protocol . '://' . \strtolower($host) . ':' . $port;
+ }
+
+ /**
+ * Check whether the given URLs have the same protocol, domain and port.
+ * This is useful to check a browser's cross-domain situation.
+ * If this method returns false, a browser would consider both URLs to be
+ * a cross-domain situation and would require a CORS setup.
+ *
+ * @param string $url1
+ * @param string $url2
+ *
+ * @return bool true if both URLs have the same protocol, domain and port
+ *
+ * @since 28.0.0
+ */
+ public static function isSameDomain(string $url1, string $url2): bool {
+ return self::getFullDomain($url1) === self::getFullDomain($url2);
+ }
}
diff --git a/settings/Controller/CorsController.php b/settings/Controller/CorsController.php
new file mode 100644
index 00000000000..cd3e82643a7
--- /dev/null
+++ b/settings/Controller/CorsController.php
@@ -0,0 +1,156 @@
+
+ */
+
+namespace OC\Settings\Controller;
+
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\RedirectResponse;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\ILogger;
+use OCP\IRequest;
+use OCP\IURLGenerator;
+use OCP\IConfig;
+use OCP\IUserSession;
+
+/**
+ * This controller is responsible for managing white-listed domains for CORS
+ *
+ * @package OC\Settings\Controller
+ */
+class CorsController extends Controller {
+ /** @var ILogger */
+ private $logger;
+
+ /** @var IURLGenerator */
+ private $urlGenerator;
+
+ /** @var string */
+ private $userId;
+
+ /** @var IConfig */
+ private $config;
+
+ /**
+ * CorsController constructor.
+ *
+ * @param string $AppName The app's name.
+ * @param IRequest $request The request.
+ * @param IUserSession $userSession Logged in user's session
+ * @param ILogger $logger The logger.
+ * @param IURLGenerator $urlGenerator Use for url generation
+ * @param IConfig $config
+ */
+ public function __construct($AppName, IRequest $request,
+ IUserSession $userSession,
+ ILogger $logger,
+ IURLGenerator $urlGenerator,
+ IConfig $config) {
+ parent::__construct($AppName, $request);
+
+ $this->AppName = $AppName;
+ $this->config = $config;
+ $this->userId = $userSession->getUser()->getUID();
+ $this->logger = $logger;
+ $this->urlGenerator = $urlGenerator;
+ }
+
+ /**
+ * Returns a redirect response
+ * @return RedirectResponse
+ */
+ private function getRedirectResponse() {
+ return new RedirectResponse(
+ $this->urlGenerator->linkToRouteAbsolute(
+ 'settings.SettingsPage.getPersonal',
+ ['sectionid' => 'security']
+ ) . '#cors'
+ );
+ }
+
+ /**
+ * Gets all White-listed domains
+ *
+ * @return JSONResponse All the White-listed domains
+ */
+ public function getDomains() {
+ $userId = $this->userId;
+
+ if (empty($this->config->getUserValue($userId, 'core', 'domains'))) {
+ $domains = [];
+ } else {
+ $domains = json_decode($this->config->getUserValue($userId, 'core', 'domains'));
+ }
+
+ return new JSONResponse($domains);
+ }
+
+ /**
+ * WhiteLists a domain for CORS
+ *
+ * @param string $domain The domain to whitelist
+ * @return RedirectResponse Redirection to the settings page.
+ */
+ public function addDomain($domain) {
+ if (!isset($domain) || !self::isValidUrl($domain)) {
+ return $this->getRedirectResponse();
+ }
+
+ $userId = $this->userId;
+ $domains = json_decode($this->config->getUserValue($userId, 'core', 'domains'));
+ $domains = array_filter($domains);
+ array_push($domains, $domain);
+
+ // In case same domain is added
+ $domains = array_unique($domains);
+
+ // Store as comma seperated string
+ $domainsString = json_encode($domains);
+
+ $this->config->setUserValue($userId, 'core', 'domains', $domainsString);
+ $this->logger->debug("The domain {$domain} has been white-listed.", ['app' => $this->appName]);
+
+ return $this->getRedirectResponse();
+ }
+
+ /**
+ * Removes a WhiteListed Domain
+ *
+ * @param string $domain Domain to remove
+ * @return RedirectResponse Redirection to the settings page.
+ */
+ public function removeDomain($id) {
+ $userId = $this->userId;
+ $domains = json_decode($this->config->getUserValue($userId, 'core', 'domains'));
+
+ if ($id >= 0 && $id < count($domains)) {
+ unset($domains[$id]);
+ $this->config->setUserValue($userId, 'core', 'domains', json_encode($domains));
+ }
+
+ return $this->getRedirectResponse();
+ }
+
+ /**
+ * Checks whether a URL is valid
+ * @param string $url URL to check
+ * @return boolean whether URL is valid
+ */
+ private static function isValidUrl($url) {
+ return (filter_var($url, FILTER_VALIDATE_URL) !== false);
+ }
+}
diff --git a/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php
index 80c2ed84451..30d4eeda79e 100644
--- a/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php
+++ b/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php
@@ -15,12 +15,12 @@ use OC\AppFramework\Http\Request;
use OC\AppFramework\Middleware\Security\CORSMiddleware;
use OC\AppFramework\Middleware\Security\Exceptions\SecurityException;
use OC\AppFramework\Utility\ControllerMethodReflector;
-use OC\User\Session;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Response;
use OCP\IConfig;
use OCP\IRequest;
use OCP\IRequestId;
+use OCP\IUserSession;
use OCP\Security\Bruteforce\IThrottler;
use PHPUnit\Framework\MockObject\MockObject;
use Test\AppFramework\Middleware\Security\Mock\CORSMiddlewareController;
@@ -28,17 +28,25 @@ use Test\AppFramework\Middleware\Security\Mock\CORSMiddlewareController;
class CORSMiddlewareTest extends \Test\TestCase {
/** @var ControllerMethodReflector */
private $reflector;
- /** @var Session|MockObject */
+ /** @var IUserSession|MockObject */
private $session;
/** @var IThrottler|MockObject */
private $throttler;
+ /** @var IConfig|MockObject */
+ private $config;
/** @var CORSMiddlewareController */
private $controller;
protected function setUp(): void {
parent::setUp();
+
+ /** @var MockObject */
+ $this->config = $this->createMock(IConfig::class);
+ $this->config->method('getUserValue')->willReturn('["http:\/\/www.test.com"]');
+ $this->config->method('setUserValue')->willReturn(true);
+
$this->reflector = new ControllerMethodReflector();
- $this->session = $this->createMock(Session::class);
+ $this->session = $this->createMock(IUserSession::class);
$this->throttler = $this->createMock(IThrottler::class);
$this->controller = new CORSMiddlewareController(
'test',
@@ -51,6 +59,17 @@ class CORSMiddlewareTest extends \Test\TestCase {
['testSetCORSAPIHeader'],
['testSetCORSAPIHeaderAttribute'],
];
+
+ $this->session = $this->getMockBuilder('\OC\User\Session')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')->willReturn('user');
+ $userSession = $this->createMock(IUserSession::class);
+ $userSession->method('getUser')->willReturn($user);
+
+ $this->session = $userSession;
}
/**
@@ -60,18 +79,24 @@ class CORSMiddlewareTest extends \Test\TestCase {
$request = new Request(
[
'server' => [
- 'HTTP_ORIGIN' => 'test'
+ 'HTTP_ORIGIN' => 'http://www.test.com'
]
],
$this->createMock(IRequestId::class),
$this->createMock(IConfig::class)
);
$this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
+ $middleware = new CORSMiddleware(
+ $request,
+ $this->reflector,
+ $this->session,
+ $this->throttler,
+ $this->config
+ );
$response = $middleware->afterController($this->controller, $method, new Response());
$headers = $response->getHeaders();
- $this->assertEquals('test', $headers['Access-Control-Allow-Origin']);
+ $this->assertEquals('http://www.test.com', $headers['Access-Control-Allow-Origin']);
}
public function testNoAnnotationNoCORSHEADER(): void {
@@ -84,7 +109,7 @@ class CORSMiddlewareTest extends \Test\TestCase {
$this->createMock(IRequestId::class),
$this->createMock(IConfig::class)
);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
+ $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->config);
$response = $middleware->afterController($this->controller, __FUNCTION__, new Response());
$headers = $response->getHeaders();
@@ -108,7 +133,7 @@ class CORSMiddlewareTest extends \Test\TestCase {
$this->createMock(IConfig::class)
);
$this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
+ $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->config);
$response = $middleware->afterController($this->controller, $method, new Response());
$headers = $response->getHeaders();
@@ -131,14 +156,14 @@ class CORSMiddlewareTest extends \Test\TestCase {
$request = new Request(
[
'server' => [
- 'HTTP_ORIGIN' => 'test'
+ 'HTTP_ORIGIN' => 'http://www.test.com',
]
],
$this->createMock(IRequestId::class),
$this->createMock(IConfig::class)
);
$this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
+ $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->config);
$response = new Response();
$response->addHeader('AcCess-control-Allow-Credentials ', 'TRUE');
@@ -164,7 +189,7 @@ class CORSMiddlewareTest extends \Test\TestCase {
$this->createMock(IConfig::class)
);
$this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
+ $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->config);
$this->session->expects($this->once())
->method('isLoggedIn')
->willReturn(false);
@@ -198,7 +223,7 @@ class CORSMiddlewareTest extends \Test\TestCase {
$this->createMock(IConfig::class)
);
$this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
+ $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->config);
$this->session->expects($this->once())
->method('isLoggedIn')
->willReturn(true);
@@ -239,7 +264,7 @@ class CORSMiddlewareTest extends \Test\TestCase {
->with($this->equalTo('user'), $this->equalTo('pass'))
->willReturn(true);
$this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
+ $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->config);
$middleware->beforeController($this->controller, $method);
}
@@ -272,7 +297,7 @@ class CORSMiddlewareTest extends \Test\TestCase {
->with($this->equalTo('user'), $this->equalTo('pass'))
->will($this->throwException(new \OC\Authentication\Exceptions\PasswordLoginForbiddenException));
$this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
+ $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->config);
$middleware->beforeController($this->controller, $method);
}
@@ -305,7 +330,7 @@ class CORSMiddlewareTest extends \Test\TestCase {
->with($this->equalTo('user'), $this->equalTo('pass'))
->willReturn(false);
$this->reflector->reflect($this->controller, $method);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
+ $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->config);
$middleware->beforeController($this->controller, $method);
}
@@ -319,7 +344,7 @@ class CORSMiddlewareTest extends \Test\TestCase {
$this->createMock(IRequestId::class),
$this->createMock(IConfig::class)
);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
+ $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->config);
$response = $middleware->afterException($this->controller, __FUNCTION__, new SecurityException('A security exception'));
$expected = new JSONResponse(['message' => 'A security exception'], 500);
@@ -335,7 +360,7 @@ class CORSMiddlewareTest extends \Test\TestCase {
$this->createMock(IRequestId::class),
$this->createMock(IConfig::class)
);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
+ $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->config);
$response = $middleware->afterException($this->controller, __FUNCTION__, new SecurityException('A security exception', 501));
$expected = new JSONResponse(['message' => 'A security exception'], 501);
@@ -354,7 +379,7 @@ class CORSMiddlewareTest extends \Test\TestCase {
$this->createMock(IRequestId::class),
$this->createMock(IConfig::class)
);
- $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler);
+ $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->config);
$middleware->afterException($this->controller, __FUNCTION__, new \Exception('A regular exception'));
}
}