aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/AppFramework
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/AppFramework')
-rw-r--r--lib/private/AppFramework/Bootstrap/Coordinator.php19
-rw-r--r--lib/private/AppFramework/Bootstrap/RegistrationContext.php2
-rw-r--r--lib/private/AppFramework/Middleware/NotModifiedMiddleware.php2
-rw-r--r--lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php2
-rw-r--r--lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php4
-rw-r--r--lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php3
-rw-r--r--lib/private/AppFramework/Routing/RouteConfig.php279
-rw-r--r--lib/private/AppFramework/Routing/RouteParser.php4
-rw-r--r--lib/private/AppFramework/Services/AppConfig.php4
-rw-r--r--lib/private/AppFramework/Utility/SimpleContainer.php43
10 files changed, 54 insertions, 308 deletions
diff --git a/lib/private/AppFramework/Bootstrap/Coordinator.php b/lib/private/AppFramework/Bootstrap/Coordinator.php
index 2b04d291730..190244051d3 100644
--- a/lib/private/AppFramework/Bootstrap/Coordinator.php
+++ b/lib/private/AppFramework/Bootstrap/Coordinator.php
@@ -20,6 +20,7 @@ use OCP\Dashboard\IManager;
use OCP\Diagnostics\IEventLogger;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IServerContainer;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Log\LoggerInterface;
use Throwable;
use function class_exists;
@@ -69,26 +70,32 @@ class Coordinator {
*/
try {
$path = $this->appManager->getAppPath($appId);
+ OC_App::registerAutoloading($appId, $path);
} catch (AppPathNotFoundException) {
// Ignore
continue;
}
- OC_App::registerAutoloading($appId, $path);
$this->eventLogger->end("bootstrap:register_app:$appId:autoloader");
/*
* Next we check if there is an application class, and it implements
* the \OCP\AppFramework\Bootstrap\IBootstrap interface
*/
- $appNameSpace = App::buildAppNamespace($appId);
+ if ($appId === 'core') {
+ $appNameSpace = 'OC\\Core';
+ } else {
+ $appNameSpace = App::buildAppNamespace($appId);
+ }
$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
+
try {
- if (class_exists($applicationClassName) && in_array(IBootstrap::class, class_implements($applicationClassName), true)) {
+ if (class_exists($applicationClassName) && is_a($applicationClassName, IBootstrap::class, true)) {
$this->eventLogger->start("bootstrap:register_app:$appId:application", "Load `Application` instance for $appId");
try {
- /** @var IBootstrap|App $application */
- $apps[$appId] = $application = $this->serverContainer->query($applicationClassName);
- } catch (QueryException $e) {
+ /** @var IBootstrap&App $application */
+ $application = $this->serverContainer->query($applicationClassName);
+ $apps[$appId] = $application;
+ } catch (ContainerExceptionInterface $e) {
// Weird, but ok
$this->eventLogger->end("bootstrap:register_app:$appId");
continue;
diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
index c3b829825c2..95ad129c466 100644
--- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php
+++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
@@ -157,7 +157,7 @@ class RegistrationContext {
/** @var ServiceRegistration<\OCP\Files\Conversion\IConversionProvider>[] */
private array $fileConversionProviders = [];
-
+
/** @var ServiceRegistration<IMailProvider>[] */
private $mailProviders = [];
diff --git a/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php b/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php
index 17b423164f6..08b30092155 100644
--- a/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php
+++ b/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php
@@ -29,7 +29,7 @@ class NotModifiedMiddleware extends Middleware {
}
$modifiedSinceHeader = $this->request->getHeader('IF_MODIFIED_SINCE');
- if ($modifiedSinceHeader !== '' && $response->getLastModified() !== null && trim($modifiedSinceHeader) === $response->getLastModified()->format(\DateTimeInterface::RFC2822)) {
+ if ($modifiedSinceHeader !== '' && $response->getLastModified() !== null && trim($modifiedSinceHeader) === $response->getLastModified()->format(\DateTimeInterface::RFC7231)) {
$response->setStatus(Http::STATUS_NOT_MODIFIED);
return $response;
}
diff --git a/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php b/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
index 2b3025fccff..b3040673d0f 100644
--- a/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
+++ b/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
@@ -120,7 +120,7 @@ class PublicShareMiddleware extends Middleware {
private function throttle($bruteforceProtectionAction, $token): void {
$ip = $this->request->getRemoteAddress();
- $this->throttler->sleepDelay($ip, $bruteforceProtectionAction);
+ $this->throttler->sleepDelayOrThrowOnMax($ip, $bruteforceProtectionAction);
$this->throttler->registerAttempt($bruteforceProtectionAction, $ip, ['token' => $token]);
}
}
diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
index 7e950f2c976..edf25c2cbe7 100644
--- a/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
+++ b/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
@@ -14,7 +14,7 @@ use OCP\AppFramework\Http;
* @package OC\AppFramework\Middleware\Security\Exceptions
*/
class NotConfirmedException extends SecurityException {
- public function __construct() {
- parent::__construct('Password confirmation is required', Http::STATUS_FORBIDDEN);
+ public function __construct(string $message = 'Password confirmation is required') {
+ parent::__construct($message, Http::STATUS_FORBIDDEN);
}
}
diff --git a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
index d00840084a3..e65fe94d608 100644
--- a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
@@ -79,6 +79,9 @@ class PasswordConfirmationMiddleware extends Middleware {
if ($this->isPasswordConfirmationStrict($reflectionMethod)) {
$authHeader = $this->request->getHeader('Authorization');
+ if (!str_starts_with(strtolower($authHeader), 'basic ')) {
+ throw new NotConfirmedException('Required authorization header missing');
+ }
[, $password] = explode(':', base64_decode(substr($authHeader, 6)), 2);
$loginName = $this->session->get('loginname');
$loginResult = $this->userManager->checkPassword($loginName, $password);
diff --git a/lib/private/AppFramework/Routing/RouteConfig.php b/lib/private/AppFramework/Routing/RouteConfig.php
deleted file mode 100644
index 2b7f21a8ba5..00000000000
--- a/lib/private/AppFramework/Routing/RouteConfig.php
+++ /dev/null
@@ -1,279 +0,0 @@
-<?php
-
-declare(strict_types=1);
-/**
- * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-namespace OC\AppFramework\Routing;
-
-use OC\AppFramework\DependencyInjection\DIContainer;
-use OC\Route\Router;
-
-/**
- * Class RouteConfig
- * @package OC\AppFramework\routing
- */
-class RouteConfig {
- /** @var DIContainer */
- private $container;
-
- /** @var Router */
- private $router;
-
- /** @var array */
- private $routes;
-
- /** @var string */
- private $appName;
-
- /** @var string[] */
- private $controllerNameCache = [];
-
- protected $rootUrlApps = [
- 'cloud_federation_api',
- 'core',
- 'files_sharing',
- 'files',
- 'profile',
- 'settings',
- 'spreed',
- ];
-
- /**
- * @param \OC\AppFramework\DependencyInjection\DIContainer $container
- * @param \OC\Route\Router $router
- * @param array $routes
- * @internal param $appName
- */
- public function __construct(DIContainer $container, Router $router, $routes) {
- $this->routes = $routes;
- $this->container = $container;
- $this->router = $router;
- $this->appName = $container['AppName'];
- }
-
- /**
- * The routes and resource will be registered to the \OCP\Route\IRouter
- */
- public function register() {
- // parse simple
- $this->processIndexRoutes($this->routes);
-
- // parse resources
- $this->processIndexResources($this->routes);
-
- /*
- * OCS routes go into a different collection
- */
- $oldCollection = $this->router->getCurrentCollection();
- $this->router->useCollection($oldCollection . '.ocs');
-
- // parse ocs simple routes
- $this->processOCS($this->routes);
-
- // parse ocs simple routes
- $this->processOCSResources($this->routes);
-
- $this->router->useCollection($oldCollection);
- }
-
- private function processOCS(array $routes): void {
- $ocsRoutes = $routes['ocs'] ?? [];
- foreach ($ocsRoutes as $ocsRoute) {
- $this->processRoute($ocsRoute, 'ocs.');
- }
- }
-
- /**
- * Creates one route base on the give configuration
- * @param array $routes
- * @throws \UnexpectedValueException
- */
- private function processIndexRoutes(array $routes): void {
- $simpleRoutes = $routes['routes'] ?? [];
- foreach ($simpleRoutes as $simpleRoute) {
- $this->processRoute($simpleRoute);
- }
- }
-
- protected function processRoute(array $route, string $routeNamePrefix = ''): void {
- $name = $route['name'];
- $postfix = $route['postfix'] ?? '';
- $root = $this->buildRootPrefix($route, $routeNamePrefix);
-
- $url = $root . '/' . ltrim($route['url'], '/');
- $verb = strtoupper($route['verb'] ?? 'GET');
-
- $split = explode('#', $name, 2);
- if (count($split) !== 2) {
- throw new \UnexpectedValueException('Invalid route name: use the format foo#bar to reference FooController::bar');
- }
- [$controller, $action] = $split;
-
- $controllerName = $this->buildControllerName($controller);
- $actionName = $this->buildActionName($action);
-
- /*
- * The route name has to be lowercase, for symfony to match it correctly.
- * This is required because smyfony allows mixed casing for controller names in the routes.
- * To avoid breaking all the existing route names, registering and matching will only use the lowercase names.
- * This is also safe on the PHP side because class and method names collide regardless of the casing.
- */
- $routeName = strtolower($routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix);
-
- $router = $this->router->create($routeName, $url)
- ->method($verb);
-
- // optionally register requirements for route. This is used to
- // tell the route parser how url parameters should be matched
- if (array_key_exists('requirements', $route)) {
- $router->requirements($route['requirements']);
- }
-
- // optionally register defaults for route. This is used to
- // tell the route parser how url parameters should be default valued
- $defaults = [];
- if (array_key_exists('defaults', $route)) {
- $defaults = $route['defaults'];
- }
-
- $defaults['caller'] = [$this->appName, $controllerName, $actionName];
- $router->defaults($defaults);
- }
-
- /**
- * For a given name and url restful OCS routes are created:
- * - index
- * - show
- * - create
- * - update
- * - destroy
- *
- * @param array $routes
- */
- private function processOCSResources(array $routes): void {
- $this->processResources($routes['ocs-resources'] ?? [], 'ocs.');
- }
-
- /**
- * For a given name and url restful routes are created:
- * - index
- * - show
- * - create
- * - update
- * - destroy
- *
- * @param array $routes
- */
- private function processIndexResources(array $routes): void {
- $this->processResources($routes['resources'] ?? []);
- }
-
- /**
- * For a given name and url restful routes are created:
- * - index
- * - show
- * - create
- * - update
- * - destroy
- *
- * @param array $resources
- * @param string $routeNamePrefix
- */
- protected function processResources(array $resources, string $routeNamePrefix = ''): void {
- // declaration of all restful actions
- $actions = [
- ['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
- ['name' => 'show', 'verb' => 'GET'],
- ['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
- ['name' => 'update', 'verb' => 'PUT'],
- ['name' => 'destroy', 'verb' => 'DELETE'],
- ];
-
- foreach ($resources as $resource => $config) {
- $root = $this->buildRootPrefix($config, $routeNamePrefix);
-
- // the url parameter used as id to the resource
- foreach ($actions as $action) {
- $url = $root . '/' . ltrim($config['url'], '/');
- $method = $action['name'];
-
- $verb = strtoupper($action['verb'] ?? 'GET');
- $collectionAction = $action['on-collection'] ?? false;
- if (!$collectionAction) {
- $url .= '/{id}';
- }
- if (isset($action['url-postfix'])) {
- $url .= '/' . $action['url-postfix'];
- }
-
- $controller = $resource;
-
- $controllerName = $this->buildControllerName($controller);
- $actionName = $this->buildActionName($method);
-
- $routeName = $routeNamePrefix . $this->appName . '.' . strtolower($resource) . '.' . $method;
-
- $route = $this->router->create($routeName, $url)
- ->method($verb);
-
- $route->defaults(['caller' => [$this->appName, $controllerName, $actionName]]);
- }
- }
- }
-
- private function buildRootPrefix(array $route, string $routeNamePrefix): string {
- $defaultRoot = $this->appName === 'core' ? '' : '/apps/' . $this->appName;
- $root = $route['root'] ?? $defaultRoot;
-
- if ($routeNamePrefix !== '') {
- // In OCS all apps are whitelisted
- return $root;
- }
-
- if (!\in_array($this->appName, $this->rootUrlApps, true)) {
- // Only allow root URLS for some apps
- return $defaultRoot;
- }
-
- return $root;
- }
-
- /**
- * Based on a given route name the controller name is generated
- * @param string $controller
- * @return string
- */
- private function buildControllerName(string $controller): string {
- if (!isset($this->controllerNameCache[$controller])) {
- $this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
- }
- return $this->controllerNameCache[$controller];
- }
-
- /**
- * Based on the action part of the route name the controller method name is generated
- * @param string $action
- * @return string
- */
- private function buildActionName(string $action): string {
- return $this->underScoreToCamelCase($action);
- }
-
- /**
- * Underscored strings are converted to camel case strings
- * @param string $str
- * @return string
- */
- private function underScoreToCamelCase(string $str): string {
- $pattern = '/_[a-z]?/';
- return preg_replace_callback(
- $pattern,
- function ($matches) {
- return strtoupper(ltrim($matches[0], '_'));
- },
- $str);
- }
-}
diff --git a/lib/private/AppFramework/Routing/RouteParser.php b/lib/private/AppFramework/Routing/RouteParser.php
index 894a74c727b..55e58234673 100644
--- a/lib/private/AppFramework/Routing/RouteParser.php
+++ b/lib/private/AppFramework/Routing/RouteParser.php
@@ -76,7 +76,7 @@ class RouteParser {
$url = $root . '/' . ltrim($route['url'], '/');
$verb = strtoupper($route['verb'] ?? 'GET');
- $split = explode('#', $name, 2);
+ $split = explode('#', $name, 3);
if (count($split) !== 2) {
throw new \UnexpectedValueException('Invalid route name: use the format foo#bar to reference FooController::bar');
}
@@ -87,7 +87,7 @@ class RouteParser {
/*
* The route name has to be lowercase, for symfony to match it correctly.
- * This is required because smyfony allows mixed casing for controller names in the routes.
+ * This is required because symfony allows mixed casing for controller names in the routes.
* To avoid breaking all the existing route names, registering and matching will only use the lowercase names.
* This is also safe on the PHP side because class and method names collide regardless of the casing.
*/
diff --git a/lib/private/AppFramework/Services/AppConfig.php b/lib/private/AppFramework/Services/AppConfig.php
index 77c5ea4de0c..04d97738483 100644
--- a/lib/private/AppFramework/Services/AppConfig.php
+++ b/lib/private/AppFramework/Services/AppConfig.php
@@ -343,7 +343,7 @@ class AppConfig implements IAppConfig {
*
* @return array<string, string>
*/
- public function getAppInstalledVersions(): array {
- return $this->appConfig->getAppInstalledVersions();
+ public function getAppInstalledVersions(bool $onlyEnabled = false): array {
+ return $this->appConfig->getAppInstalledVersions($onlyEnabled);
}
}
diff --git a/lib/private/AppFramework/Utility/SimpleContainer.php b/lib/private/AppFramework/Utility/SimpleContainer.php
index 24918992ea3..481c12cc708 100644
--- a/lib/private/AppFramework/Utility/SimpleContainer.php
+++ b/lib/private/AppFramework/Utility/SimpleContainer.php
@@ -12,6 +12,7 @@ use Closure;
use OCP\AppFramework\QueryException;
use OCP\IContainer;
use Pimple\Container;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use ReflectionClass;
use ReflectionException;
@@ -23,8 +24,9 @@ use function class_exists;
* SimpleContainer is a simple implementation of a container on basis of Pimple
*/
class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
- /** @var Container */
- private $container;
+ public static bool $useLazyObjects = false;
+
+ private Container $container;
public function __construct() {
$this->container = new Container();
@@ -49,16 +51,29 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
/**
* @param ReflectionClass $class the class to instantiate
- * @return \stdClass the created class
+ * @return object the created class
* @suppress PhanUndeclaredClassInstanceof
*/
- private function buildClass(ReflectionClass $class) {
+ private function buildClass(ReflectionClass $class): object {
$constructor = $class->getConstructor();
if ($constructor === null) {
+ /* No constructor, return a instance directly */
return $class->newInstance();
}
+ if (PHP_VERSION_ID >= 80400 && self::$useLazyObjects) {
+ /* For PHP>=8.4, use a lazy ghost to delay constructor and dependency resolving */
+ /** @psalm-suppress UndefinedMethod */
+ return $class->newLazyGhost(function (object $object) use ($constructor): void {
+ /** @psalm-suppress DirectConstructorCall For lazy ghosts we have to call the constructor directly */
+ $object->__construct(...$this->buildClassConstructorParameters($constructor));
+ });
+ } else {
+ return $class->newInstanceArgs($this->buildClassConstructorParameters($constructor));
+ }
+ }
- return $class->newInstanceArgs(array_map(function (ReflectionParameter $parameter) {
+ private function buildClassConstructorParameters(\ReflectionMethod $constructor): array {
+ return array_map(function (ReflectionParameter $parameter) {
$parameterType = $parameter->getType();
$resolveName = $parameter->getName();
@@ -69,10 +84,10 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
}
try {
- $builtIn = $parameter->hasType() && ($parameter->getType() instanceof ReflectionNamedType)
- && $parameter->getType()->isBuiltin();
+ $builtIn = $parameterType !== null && ($parameterType instanceof ReflectionNamedType)
+ && $parameterType->isBuiltin();
return $this->query($resolveName, !$builtIn);
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
// Service not found, use the default value when available
if ($parameter->isDefaultValueAvailable()) {
return $parameter->getDefaultValue();
@@ -82,7 +97,7 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
$resolveName = $parameter->getName();
try {
return $this->query($resolveName);
- } catch (QueryException $e2) {
+ } catch (ContainerExceptionInterface $e2) {
// Pass null if typed and nullable
if ($parameter->allowsNull() && ($parameterType instanceof ReflectionNamedType)) {
return null;
@@ -95,7 +110,7 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
throw $e;
}
- }, $constructor->getParameters()));
+ }, $constructor->getParameters());
}
public function resolve($name) {
@@ -153,13 +168,13 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
return $closure($this);
};
$name = $this->sanitizeName($name);
- if (isset($this[$name])) {
- unset($this[$name]);
+ if (isset($this->container[$name])) {
+ unset($this->container[$name]);
}
if ($shared) {
- $this[$name] = $wrapped;
+ $this->container[$name] = $wrapped;
} else {
- $this[$name] = $this->container->factory($wrapped);
+ $this->container[$name] = $this->container->factory($wrapped);
}
}