From 6451274db697660f403c0522021b4c8e6f19221a Mon Sep 17 00:00:00 2001 From: Tomasz Grobelny Date: Wed, 3 Apr 2019 22:43:21 +0200 Subject: [PATCH] Changes required to allow third-party Nextcloud application to use multiple requests per process approach for document previews and gallery previews Signed-off-by: Tomasz Grobelny --- lib/composer/composer/autoload_classmap.php | 1 + lib/composer/composer/autoload_static.php | 1 + lib/private/AppFramework/App.php | 3 +- .../DependencyInjection/DIContainer.php | 12 ++------ lib/private/AppFramework/Http/Dispatcher.php | 28 ++++++++--------- .../Middleware/MiddlewareDispatcher.php | 8 +++++ .../Security/SameSiteCookieMiddleware.php | 20 +++++-------- .../Utility/ControllerMethodReflector.php | 4 +++ .../AppFramework/Utility/SimpleContainer.php | 16 ++++++++-- lib/private/Files/Filesystem.php | 8 +++++ lib/private/Server.php | 30 ++++++++----------- lib/private/legacy/util.php | 26 ++++++++++------ lib/public/AppFramework/HttpContext.php | 25 ++++++++++++++++ lib/public/AppFramework/Middleware.php | 5 ++++ 14 files changed, 118 insertions(+), 69 deletions(-) create mode 100644 lib/public/AppFramework/HttpContext.php diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index a0fe19e011822..3e146e231c8d8 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -31,6 +31,7 @@ 'OCP\\AppFramework\\Db\\MultipleObjectsReturnedException' => $baseDir . '/lib/public/AppFramework/Db/MultipleObjectsReturnedException.php', 'OCP\\AppFramework\\Db\\QBMapper' => $baseDir . '/lib/public/AppFramework/Db/QBMapper.php', 'OCP\\AppFramework\\Http' => $baseDir . '/lib/public/AppFramework/Http.php', + 'OCP\\AppFramework\\HttpContext' => $baseDir . '/lib/public/AppFramework/HttpContext.php', 'OCP\\AppFramework\\Http\\ContentSecurityPolicy' => $baseDir . '/lib/public/AppFramework/Http/ContentSecurityPolicy.php', 'OCP\\AppFramework\\Http\\DataDisplayResponse' => $baseDir . '/lib/public/AppFramework/Http/DataDisplayResponse.php', 'OCP\\AppFramework\\Http\\DataDownloadResponse' => $baseDir . '/lib/public/AppFramework/Http/DataDownloadResponse.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 3dcca438cb67d..a482f4e5e4d34 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -61,6 +61,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\AppFramework\\Db\\MultipleObjectsReturnedException' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/MultipleObjectsReturnedException.php', 'OCP\\AppFramework\\Db\\QBMapper' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Db/QBMapper.php', 'OCP\\AppFramework\\Http' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http.php', + 'OCP\\AppFramework\\HttpContext' => __DIR__ . '/../../..' . '/lib/public/AppFramework/HttpContext.php', 'OCP\\AppFramework\\Http\\ContentSecurityPolicy' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/ContentSecurityPolicy.php', 'OCP\\AppFramework\\Http\\DataDisplayResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/DataDisplayResponse.php', 'OCP\\AppFramework\\Http\\DataDownloadResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/DataDownloadResponse.php', diff --git a/lib/private/AppFramework/App.php b/lib/private/AppFramework/App.php index 5a9fb0c64fc5b..50f52d91728c2 100644 --- a/lib/private/AppFramework/App.php +++ b/lib/private/AppFramework/App.php @@ -123,8 +123,7 @@ public static function main(string $controllerName, string $methodName, DIContai $responseCookies, $output, $response - ) = $dispatcher->dispatch($controller, $methodName); - + ) = $dispatcher->dispatch($controller, $methodName, $container->query(IRequest::class)); $io = $container[IOutput::class]; if(!is_null($httpHeaders)) { diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php index fc16521cf3277..4b4c77ace20eb 100644 --- a/lib/private/AppFramework/DependencyInjection/DIContainer.php +++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php @@ -99,13 +99,12 @@ public function __construct($appName, $urlParams = array(), ServerContainer $ser // aliases $this->registerAlias('appName', 'AppName'); $this->registerAlias('webRoot', 'WebRoot'); - $this->registerAlias('userId', 'UserId'); /** * Core services */ $this->registerService(IOutput::class, function(){ - return new Output($this->getServer()->getWebRoot()); + return $this->getServer()->query(IOutput::class); }); $this->registerService(Folder::class, function() { @@ -138,11 +137,6 @@ public function __construct($appName, $urlParams = array(), ServerContainer $ser return $c; }); - // commonly used attributes - $this->registerService('UserId', function ($c) { - return $c->query(IUserSession::class)->getSession()->get('user_id'); - }); - $this->registerService('WebRoot', function ($c) { return $c->query('ServerContainer')->getWebRoot(); }); @@ -166,8 +160,7 @@ public function __construct($appName, $urlParams = array(), ServerContainer $ser return new Dispatcher( $c['Protocol'], $c['MiddlewareDispatcher'], - $c->query(IControllerMethodReflector::class), - $c['Request'] + $c->query(IControllerMethodReflector::class) ); }); @@ -191,7 +184,6 @@ public function __construct($appName, $urlParams = array(), ServerContainer $ser $dispatcher->registerMiddleware( new OC\AppFramework\Middleware\Security\SameSiteCookieMiddleware( - $c->query(IRequest::class), $c->query(IControllerMethodReflector::class) ) ); diff --git a/lib/private/AppFramework/Http/Dispatcher.php b/lib/private/AppFramework/Http/Dispatcher.php index 7b9ad015de602..a4ad7b910192b 100644 --- a/lib/private/AppFramework/Http/Dispatcher.php +++ b/lib/private/AppFramework/Http/Dispatcher.php @@ -37,6 +37,7 @@ use OCP\AppFramework\Controller; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\HttpContext; use OCP\IRequest; @@ -54,25 +55,19 @@ class Dispatcher { /** @var ControllerMethodReflector */ private $reflector; - /** @var IRequest */ - private $request; - /** * @param Http $protocol the http protocol with contains all status headers * @param MiddlewareDispatcher $middlewareDispatcher the dispatcher which * runs the middleware * @param ControllerMethodReflector $reflector the reflector that is used to inject * the arguments for the controller - * @param IRequest $request the incoming request */ public function __construct(Http $protocol, MiddlewareDispatcher $middlewareDispatcher, - ControllerMethodReflector $reflector, - IRequest $request) { + ControllerMethodReflector $reflector) { $this->protocol = $protocol; $this->middlewareDispatcher = $middlewareDispatcher; $this->reflector = $reflector; - $this->request = $request; } @@ -86,7 +81,7 @@ public function __construct(Http $protocol, * the response output * @throws \Exception */ - public function dispatch(Controller $controller, string $methodName): array { + public function dispatch(Controller $controller, string $methodName, IRequest $request): array { $out = [null, [], null]; try { @@ -94,9 +89,12 @@ public function dispatch(Controller $controller, string $methodName): array { // middlewares $this->reflector->reflect($controller, $methodName); + $context = new HttpContext(); + $context->request = $request; + $this->middlewareDispatcher->setContext($context); $this->middlewareDispatcher->beforeController($controller, $methodName); - $response = $this->executeController($controller, $methodName); + $response = $this->executeController($controller, $methodName, $request); // if an exception appears, the middleware checks if it can handle the // exception and creates a response. If no response is created, it is @@ -131,7 +129,7 @@ public function dispatch(Controller $controller, string $methodName): array { * @param string $methodName the method on the controller that should be executed * @return Response */ - private function executeController(Controller $controller, string $methodName): Response { + private function executeController(Controller $controller, string $methodName, IRequest $request): Response { $arguments = []; // valid types that will be casted @@ -141,7 +139,7 @@ private function executeController(Controller $controller, string $methodName): // try to get the parameter from the request object and cast // it to the type annotated in the @param annotation - $value = $this->request->getParam($param, $default); + $value = $request->getParam($param, $default); $type = $this->reflector->getType($param); // if this is submitted using GET or a POST form, 'false' should be @@ -149,8 +147,8 @@ private function executeController(Controller $controller, string $methodName): if(($type === 'bool' || $type === 'boolean') && $value === 'false' && ( - $this->request->method === 'GET' || - strpos($this->request->getHeader('Content-Type'), + $request->method === 'GET' || + strpos($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false ) ) { @@ -169,11 +167,11 @@ private function executeController(Controller $controller, string $methodName): if($response instanceof DataResponse || !($response instanceof Response)) { // get format from the url format or request format parameter - $format = $this->request->getParam('format'); + $format = $request->getParam('format'); // if none is given try the first Accept header if($format === null) { - $headers = $this->request->getHeader('Accept'); + $headers = $request->getHeader('Accept'); $format = $controller->getResponderByHTTPHeader($headers, null); } diff --git a/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php b/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php index e1262b6c71242..fec39819c16f7 100644 --- a/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php +++ b/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php @@ -32,6 +32,7 @@ use OCP\AppFramework\Controller; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; +use OCP\AppFramework\HttpContext; /** * This class is used to store and run all the middleware in correct order @@ -76,6 +77,13 @@ public function getMiddlewares(): array { return $this->middlewares; } + public function setContext(HttpContext $context){ + $middlewareCount = \count($this->middlewares); + for($i = 0; $i < $middlewareCount; $i++){ + $middleware = $this->middlewares[$i]; + $middleware->setContext($context); + } + } /** * This is being run in normal order before the controller is being diff --git a/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php b/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php index 22a9246d6617a..c68eff60881fc 100644 --- a/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php @@ -24,27 +24,23 @@ use OC\AppFramework\Http\Request; use OC\AppFramework\Middleware\Security\Exceptions\LaxSameSiteCookieFailedException; -use OC\AppFramework\Utility\ControllerMethodReflector; +use OCP\AppFramework\Utility\IControllerMethodReflector; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; +use OCP\IRequest; class SameSiteCookieMiddleware extends Middleware { - /** @var Request */ - private $request; - - /** @var ControllerMethodReflector */ + /** @var IControllerMethodReflector */ private $reflector; - public function __construct(Request $request, - ControllerMethodReflector $reflector) { - $this->request = $request; + public function __construct(IControllerMethodReflector $reflector) { $this->reflector = $reflector; } public function beforeController($controller, $methodName) { - $requestUri = $this->request->getScriptName(); + $requestUri = $this->context->request->getScriptName(); $processingScript = explode('/', $requestUri); $processingScript = $processingScript[count($processingScript)-1]; @@ -57,7 +53,7 @@ public function beforeController($controller, $methodName) { return; } - if (!$this->request->passesLaxCookieCheck()) { + if (!$this->context->request->passesLaxCookieCheck()) { throw new LaxSameSiteCookieFailedException(); } } @@ -66,7 +62,7 @@ public function afterException($controller, $methodName, \Exception $exception) if ($exception instanceof LaxSameSiteCookieFailedException) { $respone = new Response(); $respone->setStatus(Http::STATUS_FOUND); - $respone->addHeader('Location', $this->request->getRequestUri()); + $respone->addHeader('Location', $this->context->request->getRequestUri()); $this->setSameSiteCookie(); @@ -77,7 +73,7 @@ public function afterException($controller, $methodName, \Exception $exception) } protected function setSameSiteCookie() { - $cookieParams = $this->request->getCookieParams(); + $cookieParams = $this->context->request->getCookieParams(); $secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : ''; $policies = [ 'lax', diff --git a/lib/private/AppFramework/Utility/ControllerMethodReflector.php b/lib/private/AppFramework/Utility/ControllerMethodReflector.php index ef4a1959d66f6..5d4257b0328a5 100644 --- a/lib/private/AppFramework/Utility/ControllerMethodReflector.php +++ b/lib/private/AppFramework/Utility/ControllerMethodReflector.php @@ -44,6 +44,10 @@ class ControllerMethodReflector implements IControllerMethodReflector { * @param string $method the method which we want to inspect */ public function reflect($object, string $method){ + $this->annotations = []; + $this->types = []; + $this->parameters = []; + $reflection = new \ReflectionMethod($object, $method); $docs = $reflection->getDocComment(); diff --git a/lib/private/AppFramework/Utility/SimpleContainer.php b/lib/private/AppFramework/Utility/SimpleContainer.php index 6c2844e681bbd..7455045b761f4 100644 --- a/lib/private/AppFramework/Utility/SimpleContainer.php +++ b/lib/private/AppFramework/Utility/SimpleContainer.php @@ -117,13 +117,23 @@ public function query($name) { return $this->offsetGet($name); } else { $object = $this->resolve($name); - $this->registerService($name, function () use ($object) { - return $object; - }); + if (!$this->endsWith($name, 'Controller')) { + $this->registerService($name, function () use ($object) { + return $object; + }); + } return $object; } } + private function endsWith($haystack, $needle) { + $length = strlen($needle); + if ($length == 0) { + return true; + } + return (substr($haystack, -$length) === $needle); + } + /** * @param string $name * @param mixed $value diff --git a/lib/private/Files/Filesystem.php b/lib/private/Files/Filesystem.php index 6090012871744..80f89dffbcb09 100644 --- a/lib/private/Files/Filesystem.php +++ b/lib/private/Files/Filesystem.php @@ -213,6 +213,14 @@ class Filesystem { /** @var bool */ private static $logWarningWhenAddingStorageWrapper = true; + public static function reset() { + self::$loaded = false; + self::$defaultInstance = null; + self::$usersSetup = array(); + self::$normalizedPathCache = null; + self::$listeningForProviders = false; + } + /** * @param bool $shouldLog * @return bool previous value diff --git a/lib/private/Server.php b/lib/private/Server.php index 6507f58f89648..f5c386c99c7ae 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -47,6 +47,7 @@ namespace OC; +use OCP\AppFramework\Http\IOutput; use bantu\IniGetWrapper\IniGetWrapper; use OC\Accounts\AccountManager; use OC\App\AppManager; @@ -56,6 +57,7 @@ use OC\AppFramework\Http\Request; use OC\AppFramework\Utility\SimpleContainer; use OC\AppFramework\Utility\TimeFactory; +use OC\AppFramework\Http\Output; use OC\Authentication\LoginCredentials\Store; use OC\Authentication\Token\IProvider; use OC\Avatar\AvatarManager; @@ -161,6 +163,7 @@ use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; +use OCP\IUserSession; /** * Class Server @@ -184,6 +187,11 @@ public function __construct($webRoot, \OC\Config $config) { // To find out if we are running from CLI or not $this->registerParameter('isCLI', \OC::$CLI); + $this->registerService('UserId', function ($c) { + return $c->query(IUserSession::class)->getSession()->get('user_id'); + }); + $this->registerAlias('userId', 'UserId'); + $this->registerService(\OCP\IServerContainer::class, function (IServerContainer $c) { return $c; }); @@ -996,30 +1004,16 @@ public function __construct($webRoot, \OC\Config $config) { $this->registerAlias(EventDispatcherInterface::class, EventDispatcher::class); $this->registerService('CryptoWrapper', function (Server $c) { - // FIXME: Instantiiated here due to cyclic dependency - $request = new Request( - [ - 'get' => $_GET, - 'post' => $_POST, - 'files' => $_FILES, - 'server' => $_SERVER, - 'env' => $_ENV, - 'cookies' => $_COOKIE, - 'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD'])) - ? $_SERVER['REQUEST_METHOD'] - : null, - ], - $c->getSecureRandom(), - $c->getConfig() - ); - return new CryptoWrapper( $c->getConfig(), $c->getCrypto(), $c->getSecureRandom(), - $request + $c->getRequest() ); }); + $this->registerService(IOutput::class, function(Server $c) { + return new Output($c->getWebRoot()); + }); $this->registerService('CsrfTokenManager', function (Server $c) { $tokenGenerator = new CsrfTokenGenerator($c->getSecureRandom()); diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php index 9bada5bb733be..8d6c9e2569761 100644 --- a/lib/private/legacy/util.php +++ b/lib/private/legacy/util.php @@ -184,13 +184,7 @@ public static function setupFS($user = '') { } \OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem'); - - // If we are not forced to load a specific user we load the one that is logged in - if ($user === null) { - $user = ''; - } else if ($user == "" && \OC::$server->getUserSession()->isLoggedIn()) { - $user = OC_User::getUser(); - } + $user = OC_Util::getUser($user); // load all filesystem apps before, so no setup-hook gets lost OC_App::loadApps(array('filesystem')); @@ -298,7 +292,13 @@ public static function setupFS($user = '') { \OC::$server->getEventLogger()->end('setup_fs'); return false; } + OC_Util::initFs($user); + \OC::$server->getEventLogger()->end('setup_fs'); + return true; + } + public static function initFs($user = '') { + $user = OC_Util::getUser($user); //if we aren't logged in, there is no use to set up the filesystem if ($user != "") { @@ -309,8 +309,16 @@ public static function setupFS($user = '') { OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $userDir)); } - \OC::$server->getEventLogger()->end('setup_fs'); - return true; + } + + private static function getUser($user) { + // If we are not forced to load a specific user we load the one that is logged in + if ($user === null) { + $user = ''; + } else if ($user == "" && \OC::$server->getUserSession()->isLoggedIn()) { + $user = OC_User::getUser(); + } + return $user; } /** diff --git a/lib/public/AppFramework/HttpContext.php b/lib/public/AppFramework/HttpContext.php new file mode 100644 index 0000000000000..c8d5d9552024d --- /dev/null +++ b/lib/public/AppFramework/HttpContext.php @@ -0,0 +1,25 @@ + + * + */ + +namespace OCP\AppFramework; + +use OCP\AppFramework\Http\Response; +use OCP\IRequest; + +class HttpContext { + /** @var IRequest */ + public $request; +} diff --git a/lib/public/AppFramework/Middleware.php b/lib/public/AppFramework/Middleware.php index 4c9051a0b3479..5406bd8cc7700 100644 --- a/lib/public/AppFramework/Middleware.php +++ b/lib/public/AppFramework/Middleware.php @@ -43,6 +43,11 @@ */ abstract class Middleware { + protected $context; + + public function setContext(HttpContext $context) { + $this->context = $context; + } /** * This is being run in normal order before the controller is being