aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/AppFramework
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@users.noreply.github.com>2024-02-23 15:47:17 +0100
committerGitHub <noreply@github.com>2024-02-23 15:47:17 +0100
commitb5357f7d12f89ce965cf8b8dd3bbc9cd0ad042c6 (patch)
treefb5982f6df0546adacb28cb67f96148dcfe78b33 /lib/private/AppFramework
parentce74bdcda244172cbe90dc792e30128802a78828 (diff)
parenta88c1bdfb61d4c141d90e6864971f6d456417604 (diff)
downloadnextcloud-server-b5357f7d12f89ce965cf8b8dd3bbc9cd0ad042c6.tar.gz
nextcloud-server-b5357f7d12f89ce965cf8b8dd3bbc9cd0ad042c6.zip
Merge branch 'master' into refactor/OC-Server-getThemingDefaults
Signed-off-by: John Molakvoæ <skjnldsv@users.noreply.github.com>
Diffstat (limited to 'lib/private/AppFramework')
-rw-r--r--lib/private/AppFramework/App.php8
-rw-r--r--lib/private/AppFramework/Bootstrap/Coordinator.php10
-rw-r--r--lib/private/AppFramework/Bootstrap/EventListenerRegistration.php6
-rw-r--r--lib/private/AppFramework/Bootstrap/ParameterRegistration.php4
-rw-r--r--lib/private/AppFramework/Bootstrap/PreviewProviderRegistration.php4
-rw-r--r--lib/private/AppFramework/Bootstrap/RegistrationContext.php76
-rw-r--r--lib/private/AppFramework/Bootstrap/ServiceAliasRegistration.php4
-rw-r--r--lib/private/AppFramework/Bootstrap/ServiceFactoryRegistration.php6
-rw-r--r--lib/private/AppFramework/DependencyInjection/DIContainer.php28
-rw-r--r--lib/private/AppFramework/Http/Dispatcher.php38
-rw-r--r--lib/private/AppFramework/Http/Request.php55
-rw-r--r--lib/private/AppFramework/Http/RequestId.php2
-rw-r--r--lib/private/AppFramework/Middleware/MiddlewareDispatcher.php12
-rw-r--r--lib/private/AppFramework/Middleware/Security/CORSMiddleware.php11
-rw-r--r--lib/private/AppFramework/Middleware/Security/CSPMiddleware.php4
-rw-r--r--lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php3
-rw-r--r--lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php6
-rw-r--r--lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php2
-rw-r--r--lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php30
-rw-r--r--lib/private/AppFramework/Middleware/SessionMiddleware.php2
-rw-r--r--lib/private/AppFramework/OCS/BaseResponse.php8
-rw-r--r--lib/private/AppFramework/Routing/RouteConfig.php8
-rw-r--r--lib/private/AppFramework/Routing/RouteParser.php8
-rw-r--r--lib/private/AppFramework/ScopedPsrLogger.php2
-rw-r--r--lib/private/AppFramework/Services/AppConfig.php309
-rw-r--r--lib/private/AppFramework/Utility/ControllerMethodReflector.php35
-rw-r--r--lib/private/AppFramework/Utility/SimpleContainer.php7
-rw-r--r--lib/private/AppFramework/Utility/TimeFactory.php9
28 files changed, 541 insertions, 156 deletions
diff --git a/lib/private/AppFramework/App.php b/lib/private/AppFramework/App.php
index ffd77da888e..b18c95a2f0d 100644
--- a/lib/private/AppFramework/App.php
+++ b/lib/private/AppFramework/App.php
@@ -34,16 +34,16 @@ namespace OC\AppFramework;
use OC\AppFramework\DependencyInjection\DIContainer;
use OC\AppFramework\Http\Dispatcher;
use OC\AppFramework\Http\Request;
-use OCP\App\IAppManager;
-use OCP\Profiler\IProfiler;
use OC\Profiler\RoutingDataCollector;
-use OCP\AppFramework\QueryException;
+use OCP\App\IAppManager;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\ICallbackResponse;
use OCP\AppFramework\Http\IOutput;
+use OCP\AppFramework\QueryException;
use OCP\Diagnostics\IEventLogger;
use OCP\HintException;
use OCP\IRequest;
+use OCP\Profiler\IProfiler;
/**
* Entry point for every request in your app. You can consider this as your
@@ -257,7 +257,7 @@ class App {
* @param DIContainer $container an instance of a pimple container.
*/
public static function part(string $controllerName, string $methodName, array $urlParams,
- DIContainer $container) {
+ DIContainer $container) {
$container['urlParams'] = $urlParams;
$controller = $container[$controllerName];
diff --git a/lib/private/AppFramework/Bootstrap/Coordinator.php b/lib/private/AppFramework/Bootstrap/Coordinator.php
index f41b734a25b..8526a3dc1a1 100644
--- a/lib/private/AppFramework/Bootstrap/Coordinator.php
+++ b/lib/private/AppFramework/Bootstrap/Coordinator.php
@@ -30,20 +30,20 @@ declare(strict_types=1);
namespace OC\AppFramework\Bootstrap;
-use OCP\Diagnostics\IEventLogger;
-use function class_exists;
-use function class_implements;
-use function in_array;
-use OC_App;
use OC\Support\CrashReport\Registry;
+use OC_App;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\QueryException;
use OCP\Dashboard\IManager;
+use OCP\Diagnostics\IEventLogger;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IServerContainer;
use Psr\Log\LoggerInterface;
use Throwable;
+use function class_exists;
+use function class_implements;
+use function in_array;
class Coordinator {
/** @var IServerContainer */
diff --git a/lib/private/AppFramework/Bootstrap/EventListenerRegistration.php b/lib/private/AppFramework/Bootstrap/EventListenerRegistration.php
index 2ad410be26f..12801e62763 100644
--- a/lib/private/AppFramework/Bootstrap/EventListenerRegistration.php
+++ b/lib/private/AppFramework/Bootstrap/EventListenerRegistration.php
@@ -37,9 +37,9 @@ class EventListenerRegistration extends ServiceRegistration {
private $priority;
public function __construct(string $appId,
- string $event,
- string $service,
- int $priority) {
+ string $event,
+ string $service,
+ int $priority) {
parent::__construct($appId, $service);
$this->event = $event;
$this->priority = $priority;
diff --git a/lib/private/AppFramework/Bootstrap/ParameterRegistration.php b/lib/private/AppFramework/Bootstrap/ParameterRegistration.php
index b501a757abd..958f24cb600 100644
--- a/lib/private/AppFramework/Bootstrap/ParameterRegistration.php
+++ b/lib/private/AppFramework/Bootstrap/ParameterRegistration.php
@@ -36,8 +36,8 @@ final class ParameterRegistration extends ARegistration {
private $value;
public function __construct(string $appId,
- string $name,
- $value) {
+ string $name,
+ $value) {
parent::__construct($appId);
$this->name = $name;
$this->value = $value;
diff --git a/lib/private/AppFramework/Bootstrap/PreviewProviderRegistration.php b/lib/private/AppFramework/Bootstrap/PreviewProviderRegistration.php
index 36c5cae7db3..e4d75f75bc8 100644
--- a/lib/private/AppFramework/Bootstrap/PreviewProviderRegistration.php
+++ b/lib/private/AppFramework/Bootstrap/PreviewProviderRegistration.php
@@ -34,8 +34,8 @@ class PreviewProviderRegistration extends ServiceRegistration {
private $mimeTypeRegex;
public function __construct(string $appId,
- string $service,
- string $mimeTypeRegex) {
+ string $service,
+ string $mimeTypeRegex) {
parent::__construct($appId, $service);
$this->mimeTypeRegex = $mimeTypeRegex;
}
diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
index 5aea2a7a744..120ee7ea9fa 100644
--- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php
+++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
@@ -30,15 +30,6 @@ declare(strict_types=1);
namespace OC\AppFramework\Bootstrap;
use Closure;
-use OCP\Calendar\Resource\IBackend as IResourceBackend;
-use OCP\Calendar\Room\IBackend as IRoomBackend;
-use OCP\Collaboration\Reference\IReferenceProvider;
-use OCP\TextProcessing\IProvider as ITextProcessingProvider;
-use OCP\SpeechToText\ISpeechToTextProvider;
-use OCP\Talk\ITalkBackend;
-use OCP\Translation\ITranslationProvider;
-use RuntimeException;
-use function array_shift;
use OC\Support\CrashReport\Registry;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
@@ -46,7 +37,10 @@ use OCP\AppFramework\Middleware;
use OCP\AppFramework\Services\InitialStateProvider;
use OCP\Authentication\IAlternativeLogin;
use OCP\Calendar\ICalendarProvider;
+use OCP\Calendar\Resource\IBackend as IResourceBackend;
+use OCP\Calendar\Room\IBackend as IRoomBackend;
use OCP\Capabilities\ICapability;
+use OCP\Collaboration\Reference\IReferenceProvider;
use OCP\Dashboard\IManager;
use OCP\Dashboard\IWidget;
use OCP\EventDispatcher\IEventDispatcher;
@@ -55,11 +49,18 @@ use OCP\Http\WellKnown\IHandler;
use OCP\Notification\INotifier;
use OCP\Profile\ILinkAction;
use OCP\Search\IProvider;
+use OCP\SetupCheck\ISetupCheck;
use OCP\Share\IPublicShareTemplateProvider;
+use OCP\SpeechToText\ISpeechToTextProvider;
use OCP\Support\CrashReport\IReporter;
+use OCP\Talk\ITalkBackend;
+use OCP\TextProcessing\IProvider as ITextProcessingProvider;
+use OCP\Translation\ITranslationProvider;
use OCP\UserMigration\IMigrator as IUserMigrator;
use Psr\Log\LoggerInterface;
+use RuntimeException;
use Throwable;
+use function array_shift;
class RegistrationContext {
/** @var ServiceRegistration<ICapability>[] */
@@ -137,6 +138,9 @@ class RegistrationContext {
/** @var ServiceRegistration<IReferenceProvider>[] */
private array $referenceProviders = [];
+ /** @var ServiceRegistration<\OCP\TextToImage\IProvider>[] */
+ private $textToImageProviders = [];
+
@@ -146,11 +150,13 @@ class RegistrationContext {
/** @var ServiceRegistration<IPublicShareTemplateProvider>[] */
private $publicShareTemplateProviders = [];
- /** @var LoggerInterface */
- private $logger;
+ private LoggerInterface $logger;
+
+ /** @var ServiceRegistration<ISetupCheck>[] */
+ private array $setupChecks = [];
/** @var PreviewProviderRegistration[] */
- private $previewProviders = [];
+ private array $previewProviders = [];
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
@@ -273,6 +279,13 @@ class RegistrationContext {
);
}
+ public function registerTextToImageProvider(string $providerClass): void {
+ $this->context->registerTextToImageProvider(
+ $this->appId,
+ $providerClass
+ );
+ }
+
public function registerTemplateProvider(string $providerClass): void {
$this->context->registerTemplateProvider(
$this->appId,
@@ -372,6 +385,13 @@ class RegistrationContext {
$class
);
}
+
+ public function registerSetupCheck(string $setupCheckClass): void {
+ $this->context->registerSetupCheck(
+ $this->appId,
+ $setupCheckClass
+ );
+ }
};
}
@@ -383,14 +403,14 @@ class RegistrationContext {
}
/**
- * @psalm-param class-string<IReporter> $capability
+ * @psalm-param class-string<IReporter> $reporterClass
*/
public function registerCrashReporter(string $appId, string $reporterClass): void {
$this->crashReporters[] = new ServiceRegistration($appId, $reporterClass);
}
/**
- * @psalm-param class-string<IWidget> $capability
+ * @psalm-param class-string<IWidget> $panelClass
*/
public function registerDashboardPanel(string $appId, string $panelClass): void {
$this->dashboardPanels[] = new ServiceRegistration($appId, $panelClass);
@@ -443,6 +463,10 @@ class RegistrationContext {
$this->textProcessingProviders[] = new ServiceRegistration($appId, $class);
}
+ public function registerTextToImageProvider(string $appId, string $class): void {
+ $this->textToImageProviders[] = new ServiceRegistration($appId, $class);
+ }
+
public function registerTemplateProvider(string $appId, string $class): void {
$this->templateProviders[] = new ServiceRegistration($appId, $class);
}
@@ -524,6 +548,13 @@ class RegistrationContext {
}
/**
+ * @psalm-param class-string<ISetupCheck> $setupCheckClass
+ */
+ public function registerSetupCheck(string $appId, string $setupCheckClass): void {
+ $this->setupChecks[] = new ServiceRegistration($appId, $setupCheckClass);
+ }
+
+ /**
* @param App[] $apps
*/
public function delegateCapabilityRegistrations(array $apps): void {
@@ -565,9 +596,6 @@ class RegistrationContext {
}
}
- /**
- * @param App[] $apps
- */
public function delegateDashboardPanelRegistrations(IManager $dashboardManager): void {
while (($panel = array_shift($this->dashboardPanels)) !== null) {
try {
@@ -729,6 +757,13 @@ class RegistrationContext {
}
/**
+ * @return ServiceRegistration<\OCP\TextToImage\IProvider>[]
+ */
+ public function getTextToImageProviders(): array {
+ return $this->textToImageProviders;
+ }
+
+ /**
* @return ServiceRegistration<ICustomTemplateProvider>[]
*/
public function getTemplateProviders(): array {
@@ -828,4 +863,11 @@ class RegistrationContext {
public function getPublicShareTemplateProviders(): array {
return $this->publicShareTemplateProviders;
}
+
+ /**
+ * @return ServiceRegistration<ISetupCheck>[]
+ */
+ public function getSetupChecks(): array {
+ return $this->setupChecks;
+ }
}
diff --git a/lib/private/AppFramework/Bootstrap/ServiceAliasRegistration.php b/lib/private/AppFramework/Bootstrap/ServiceAliasRegistration.php
index e2b115e0353..62c7169a7ee 100644
--- a/lib/private/AppFramework/Bootstrap/ServiceAliasRegistration.php
+++ b/lib/private/AppFramework/Bootstrap/ServiceAliasRegistration.php
@@ -46,8 +46,8 @@ class ServiceAliasRegistration extends ARegistration {
* @paslm-param string|class-string $target
*/
public function __construct(string $appId,
- string $alias,
- string $target) {
+ string $alias,
+ string $target) {
parent::__construct($appId);
$this->alias = $alias;
$this->target = $target;
diff --git a/lib/private/AppFramework/Bootstrap/ServiceFactoryRegistration.php b/lib/private/AppFramework/Bootstrap/ServiceFactoryRegistration.php
index b6658e55239..9d166526d94 100644
--- a/lib/private/AppFramework/Bootstrap/ServiceFactoryRegistration.php
+++ b/lib/private/AppFramework/Bootstrap/ServiceFactoryRegistration.php
@@ -45,9 +45,9 @@ class ServiceFactoryRegistration extends ARegistration {
private $shared;
public function __construct(string $appId,
- string $alias,
- callable $target,
- bool $shared) {
+ string $alias,
+ callable $target,
+ bool $shared) {
parent::__construct($appId);
$this->name = $alias;
$this->factory = $target;
diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php
index e4a06d9d4e0..a5273d2f335 100644
--- a/lib/private/AppFramework/DependencyInjection/DIContainer.php
+++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php
@@ -345,6 +345,7 @@ class DIContainer extends SimpleContainer implements IAppContainer {
$this->registerService(IAppConfig::class, function (ContainerInterface $c) {
return new OC\AppFramework\Services\AppConfig(
$c->get(IConfig::class),
+ $c->get(\OCP\IAppConfig::class),
$c->get('AppName')
);
});
@@ -404,33 +405,6 @@ class DIContainer extends SimpleContainer implements IAppContainer {
}
/**
- * @deprecated use the ILogger instead
- * @param string $message
- * @param string $level
- * @return mixed
- */
- public function log($message, $level) {
- switch ($level) {
- case 'debug':
- $level = ILogger::DEBUG;
- break;
- case 'info':
- $level = ILogger::INFO;
- break;
- case 'warn':
- $level = ILogger::WARN;
- break;
- case 'fatal':
- $level = ILogger::FATAL;
- break;
- default:
- $level = ILogger::ERROR;
- break;
- }
- \OCP\Util::writeLog($this->getAppName(), $message, $level);
- }
-
- /**
* Register a capability
*
* @param string $serviceName e.g. 'OCA\Files\Capabilities'
diff --git a/lib/private/AppFramework/Http/Dispatcher.php b/lib/private/AppFramework/Http/Dispatcher.php
index 13b391eb287..6e946f2bfa3 100644
--- a/lib/private/AppFramework/Http/Dispatcher.php
+++ b/lib/private/AppFramework/Http/Dispatcher.php
@@ -38,6 +38,7 @@ use OC\AppFramework\Utility\ControllerMethodReflector;
use OC\DB\ConnectionAdapter;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\ParameterOutOfRangeException;
use OCP\AppFramework\Http\Response;
use OCP\Diagnostics\IEventLogger;
use OCP\IConfig;
@@ -88,14 +89,14 @@ class Dispatcher {
* @param IEventLogger $eventLogger
*/
public function __construct(Http $protocol,
- MiddlewareDispatcher $middlewareDispatcher,
- ControllerMethodReflector $reflector,
- IRequest $request,
- IConfig $config,
- ConnectionAdapter $connection,
- LoggerInterface $logger,
- IEventLogger $eventLogger,
- ContainerInterface $appContainer) {
+ MiddlewareDispatcher $middlewareDispatcher,
+ ControllerMethodReflector $reflector,
+ IRequest $request,
+ IConfig $config,
+ ConnectionAdapter $connection,
+ LoggerInterface $logger,
+ IEventLogger $eventLogger,
+ ContainerInterface $appContainer) {
$this->protocol = $protocol;
$this->middlewareDispatcher = $middlewareDispatcher;
$this->reflector = $reflector;
@@ -197,7 +198,7 @@ class Dispatcher {
private function executeController(Controller $controller, string $methodName): Response {
$arguments = [];
- // valid types that will be casted
+ // valid types that will be cast
$types = ['int', 'integer', 'bool', 'boolean', 'float', 'double'];
foreach ($this->reflector->getParameters() as $param => $default) {
@@ -219,6 +220,7 @@ class Dispatcher {
$value = false;
} elseif ($value !== null && \in_array($type, $types, true)) {
settype($value, $type);
+ $this->ensureParameterValueSatisfiesRange($param, $value);
} elseif ($value === null && $type !== null && $this->appContainer->has($type)) {
$value = $this->appContainer->get($type);
}
@@ -250,4 +252,22 @@ class Dispatcher {
return $response;
}
+
+ /**
+ * @psalm-param mixed $value
+ * @throws ParameterOutOfRangeException
+ */
+ private function ensureParameterValueSatisfiesRange(string $param, $value): void {
+ $rangeInfo = $this->reflector->getRange($param);
+ if ($rangeInfo) {
+ if ($value < $rangeInfo['min'] || $value > $rangeInfo['max']) {
+ throw new ParameterOutOfRangeException(
+ $param,
+ $value,
+ $rangeInfo['min'],
+ $rangeInfo['max'],
+ );
+ }
+ }
+ }
}
diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php
index 408e88583a0..b905c6184fa 100644
--- a/lib/private/AppFramework/Http/Request.php
+++ b/lib/private/AppFramework/Http/Request.php
@@ -63,6 +63,7 @@ use Symfony\Component\HttpFoundation\IpUtils;
* @property string method
* @property mixed[] parameters
* @property mixed[] server
+ * @template-implements \ArrayAccess<string,mixed>
*/
class Request implements \ArrayAccess, \Countable, IRequest {
public const USER_AGENT_IE = '/(MSIE)|(Trident)/';
@@ -118,10 +119,10 @@ class Request implements \ArrayAccess, \Countable, IRequest {
* @see https://www.php.net/manual/en/reserved.variables.php
*/
public function __construct(array $vars,
- IRequestId $requestId,
- IConfig $config,
- CsrfTokenManager $csrfTokenManager = null,
- string $stream = 'php://input') {
+ IRequestId $requestId,
+ IConfig $config,
+ CsrfTokenManager $csrfTokenManager = null,
+ string $stream = 'php://input') {
$this->inputStream = $stream;
$this->items['params'] = [];
$this->requestId = $requestId;
@@ -193,9 +194,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
*/
#[\ReturnTypeWillChange]
public function offsetGet($offset) {
- return isset($this->items['parameters'][$offset])
- ? $this->items['parameters'][$offset]
- : null;
+ return $this->items['parameters'][$offset] ?? null;
}
/**
@@ -255,9 +254,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
case 'cookies':
case 'urlParams':
case 'method':
- return isset($this->items[$name])
- ? $this->items[$name]
- : null;
+ return $this->items[$name] ?? null;
case 'parameters':
case 'params':
if ($this->isPutStreamContent()) {
@@ -577,7 +574,14 @@ class Request implements \ArrayAccess, \Countable, IRequest {
* @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
*/
protected function isTrustedProxy($trustedProxies, $remoteAddress) {
- return IpUtils::checkIp($remoteAddress, $trustedProxies);
+ try {
+ return IpUtils::checkIp($remoteAddress, $trustedProxies);
+ } catch (\Throwable) {
+ // We can not log to our log here as the logger is using `getRemoteAddress` which uses the function, so we would have a cyclic dependency
+ // Reaching this line means `trustedProxies` is in invalid format.
+ error_log('Nextcloud trustedProxies has malformed entries');
+ return false;
+ }
}
/**
@@ -597,14 +601,25 @@ class Request implements \ArrayAccess, \Countable, IRequest {
// only have one default, so we cannot ship an insecure product out of the box
]);
- foreach ($forwardedForHeaders as $header) {
+ // Read the x-forwarded-for headers and values in reverse order as per
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For#selecting_an_ip_address
+ foreach (array_reverse($forwardedForHeaders) as $header) {
if (isset($this->server[$header])) {
- foreach (explode(',', $this->server[$header]) as $IP) {
+ foreach (array_reverse(explode(',', $this->server[$header])) as $IP) {
$IP = trim($IP);
+ $colons = substr_count($IP, ':');
+ if ($colons > 1) {
+ // Extract IP from string with brackets and optional port
+ if (preg_match('/^\[(.+?)\](?::\d+)?$/', $IP, $matches) && isset($matches[1])) {
+ $IP = $matches[1];
+ }
+ } elseif ($colons === 1) {
+ // IPv4 with port
+ $IP = substr($IP, 0, strpos($IP, ':'));
+ }
- // remove brackets from IPv6 addresses
- if (str_starts_with($IP, '[') && str_ends_with($IP, ']')) {
- $IP = substr($IP, 1, -1);
+ if ($this->isTrustedProxy($trustedProxies, $IP)) {
+ continue;
}
if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
@@ -620,14 +635,12 @@ class Request implements \ArrayAccess, \Countable, IRequest {
/**
* Check overwrite condition
- * @param string $type
* @return bool
*/
- private function isOverwriteCondition(string $type = ''): bool {
+ private function isOverwriteCondition(): bool {
$regex = '/' . $this->config->getSystemValueString('overwritecondaddr', '') . '/';
$remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
- return $regex === '//' || preg_match($regex, $remoteAddr) === 1
- || $type !== 'protocol';
+ return $regex === '//' || preg_match($regex, $remoteAddr) === 1;
}
/**
@@ -637,7 +650,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
*/
public function getServerProtocol(): string {
if ($this->config->getSystemValueString('overwriteprotocol') !== ''
- && $this->isOverwriteCondition('protocol')) {
+ && $this->isOverwriteCondition()) {
return $this->config->getSystemValueString('overwriteprotocol');
}
diff --git a/lib/private/AppFramework/Http/RequestId.php b/lib/private/AppFramework/Http/RequestId.php
index 70032873a75..a6b24c0a2ff 100644
--- a/lib/private/AppFramework/Http/RequestId.php
+++ b/lib/private/AppFramework/Http/RequestId.php
@@ -31,7 +31,7 @@ class RequestId implements IRequestId {
protected string $requestId;
public function __construct(string $uniqueId,
- ISecureRandom $secureRandom) {
+ ISecureRandom $secureRandom) {
$this->requestId = $uniqueId;
$this->secureRandom = $secureRandom;
}
diff --git a/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php b/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php
index 35eb0098eed..e129f70aef6 100644
--- a/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php
+++ b/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php
@@ -40,15 +40,15 @@ use OCP\AppFramework\Middleware;
*/
class MiddlewareDispatcher {
/**
- * @var array array containing all the middlewares
+ * @var Middleware[] array containing all the middlewares
*/
- private $middlewares;
+ private array $middlewares;
/**
* @var int counter which tells us what middleware was executed once an
* exception occurs
*/
- private $middlewareCounter;
+ private int $middlewareCounter;
/**
@@ -64,14 +64,14 @@ class MiddlewareDispatcher {
* Adds a new middleware
* @param Middleware $middleWare the middleware which will be added
*/
- public function registerMiddleware(Middleware $middleWare) {
+ public function registerMiddleware(Middleware $middleWare): void {
$this->middlewares[] = $middleWare;
}
/**
* returns an array with all middleware elements
- * @return array the middlewares
+ * @return Middleware[] the middlewares
*/
public function getMiddlewares(): array {
return $this->middlewares;
@@ -86,7 +86,7 @@ class MiddlewareDispatcher {
* @param string $methodName the name of the method that will be called on
* the controller
*/
- public function beforeController(Controller $controller, string $methodName) {
+ public function beforeController(Controller $controller, string $methodName): void {
// we need to count so that we know which middlewares we have to ask in
// case there is an exception
$middlewareCount = \count($this->middlewares);
diff --git a/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php b/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
index 8bdacf550b6..fef9632487e 100644
--- a/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
@@ -38,6 +38,7 @@ use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Middleware;
use OCP\IRequest;
+use OCP\ISession;
use OCP\Security\Bruteforce\IThrottler;
use ReflectionMethod;
@@ -58,9 +59,9 @@ class CORSMiddleware extends Middleware {
private $throttler;
public function __construct(IRequest $request,
- ControllerMethodReflector $reflector,
- Session $session,
- IThrottler $throttler) {
+ ControllerMethodReflector $reflector,
+ Session $session,
+ IThrottler $throttler) {
$this->request = $request;
$this->reflector = $reflector;
$this->session = $session;
@@ -91,6 +92,10 @@ class CORSMiddleware extends Middleware {
if ($this->request->passesCSRFCheck()) {
return;
}
+ // Skip CORS check for requests with AppAPI auth.
+ if ($this->session->getSession() instanceof ISession && $this->session->getSession()->get('app_api') === true) {
+ return;
+ }
$this->session->logout();
try {
if ($user === null || $pass === null || !$this->session->logClientIn($user, $pass, $this->request, $this->throttler)) {
diff --git a/lib/private/AppFramework/Middleware/Security/CSPMiddleware.php b/lib/private/AppFramework/Middleware/Security/CSPMiddleware.php
index ae0dc1f134e..60a7cef8fa1 100644
--- a/lib/private/AppFramework/Middleware/Security/CSPMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/CSPMiddleware.php
@@ -44,8 +44,8 @@ class CSPMiddleware extends Middleware {
private $csrfTokenManager;
public function __construct(ContentSecurityPolicyManager $policyManager,
- ContentSecurityPolicyNonceManager $cspNonceManager,
- CsrfTokenManager $csrfTokenManager) {
+ ContentSecurityPolicyNonceManager $cspNonceManager,
+ CsrfTokenManager $csrfTokenManager) {
$this->contentSecurityPolicyManager = $policyManager;
$this->cspNonceManager = $cspNonceManager;
$this->csrfTokenManager = $csrfTokenManager;
diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php
index 3232980b7e5..3b2296c145f 100644
--- a/lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php
+++ b/lib/private/AppFramework/Middleware/Security/Exceptions/SecurityException.php
@@ -1,4 +1,7 @@
<?php
+
+declare(strict_types=1);
+
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
*
diff --git a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
index a72a7a40016..351f47ea924 100644
--- a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
@@ -55,9 +55,9 @@ class PasswordConfirmationMiddleware extends Middleware {
* @param ITimeFactory $timeFactory
*/
public function __construct(ControllerMethodReflector $reflector,
- ISession $session,
- IUserSession $userSession,
- ITimeFactory $timeFactory) {
+ ISession $session,
+ IUserSession $userSession,
+ ITimeFactory $timeFactory) {
$this->reflector = $reflector;
$this->session = $session;
$this->userSession = $userSession;
diff --git a/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php b/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php
index e6d35dc66f2..870efdd44fa 100644
--- a/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php
@@ -38,7 +38,7 @@ class SameSiteCookieMiddleware extends Middleware {
private $reflector;
public function __construct(Request $request,
- ControllerMethodReflector $reflector) {
+ ControllerMethodReflector $reflector) {
$this->request = $request;
$this->reflector = $reflector;
}
diff --git a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
index db6c7a02c77..386075bd968 100644
--- a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
@@ -104,18 +104,18 @@ class SecurityMiddleware extends Middleware {
private $userSession;
public function __construct(IRequest $request,
- ControllerMethodReflector $reflector,
- INavigationManager $navigationManager,
- IURLGenerator $urlGenerator,
- LoggerInterface $logger,
- string $appName,
- bool $isLoggedIn,
- bool $isAdminUser,
- bool $isSubAdmin,
- IAppManager $appManager,
- IL10N $l10n,
- AuthorizedGroupMapper $mapper,
- IUserSession $userSession
+ ControllerMethodReflector $reflector,
+ INavigationManager $navigationManager,
+ IURLGenerator $urlGenerator,
+ LoggerInterface $logger,
+ string $appName,
+ bool $isLoggedIn,
+ bool $isAdminUser,
+ bool $isSubAdmin,
+ IAppManager $appManager,
+ IL10N $l10n,
+ AuthorizedGroupMapper $mapper,
+ IUserSession $userSession
) {
$this->navigationManager = $navigationManager;
$this->request = $request;
@@ -180,20 +180,20 @@ class SecurityMiddleware extends Middleware {
}
}
if (!$authorized) {
- throw new NotAdminException($this->l10n->t('Logged in user must be an admin, a sub admin or gotten special right to access this setting'));
+ throw new NotAdminException($this->l10n->t('Logged in account must be an admin, a sub admin or gotten special right to access this setting'));
}
}
if ($this->hasAnnotationOrAttribute($reflectionMethod, 'SubAdminRequired', SubAdminRequired::class)
&& !$this->isSubAdmin
&& !$this->isAdminUser
&& !$authorized) {
- throw new NotAdminException($this->l10n->t('Logged in user must be an admin or sub admin'));
+ throw new NotAdminException($this->l10n->t('Logged in account must be an admin or sub admin'));
}
if (!$this->hasAnnotationOrAttribute($reflectionMethod, 'SubAdminRequired', SubAdminRequired::class)
&& !$this->hasAnnotationOrAttribute($reflectionMethod, 'NoAdminRequired', NoAdminRequired::class)
&& !$this->isAdminUser
&& !$authorized) {
- throw new NotAdminException($this->l10n->t('Logged in user must be an admin'));
+ throw new NotAdminException($this->l10n->t('Logged in account must be an admin'));
}
}
diff --git a/lib/private/AppFramework/Middleware/SessionMiddleware.php b/lib/private/AppFramework/Middleware/SessionMiddleware.php
index 39f85915901..0acdcf8b7ef 100644
--- a/lib/private/AppFramework/Middleware/SessionMiddleware.php
+++ b/lib/private/AppFramework/Middleware/SessionMiddleware.php
@@ -44,7 +44,7 @@ class SessionMiddleware extends Middleware {
private $session;
public function __construct(ControllerMethodReflector $reflector,
- ISession $session) {
+ ISession $session) {
$this->reflector = $reflector;
$this->session = $session;
}
diff --git a/lib/private/AppFramework/OCS/BaseResponse.php b/lib/private/AppFramework/OCS/BaseResponse.php
index 123b73d302c..3cfe8177ae7 100644
--- a/lib/private/AppFramework/OCS/BaseResponse.php
+++ b/lib/private/AppFramework/OCS/BaseResponse.php
@@ -64,10 +64,10 @@ abstract class BaseResponse extends Response {
* @param int|null $itemsPerPage
*/
public function __construct(DataResponse $dataResponse,
- $format = 'xml',
- $statusMessage = null,
- $itemsCount = null,
- $itemsPerPage = null) {
+ $format = 'xml',
+ $statusMessage = null,
+ $itemsCount = null,
+ $itemsPerPage = null) {
parent::__construct();
$this->format = $format;
diff --git a/lib/private/AppFramework/Routing/RouteConfig.php b/lib/private/AppFramework/Routing/RouteConfig.php
index 6e3e49e8d99..7d63e5477ce 100644
--- a/lib/private/AppFramework/Routing/RouteConfig.php
+++ b/lib/private/AppFramework/Routing/RouteConfig.php
@@ -136,7 +136,13 @@ class RouteConfig {
$controllerName = $this->buildControllerName($controller);
$actionName = $this->buildActionName($action);
- $routeName = $routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix;
+ /*
+ * 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);
diff --git a/lib/private/AppFramework/Routing/RouteParser.php b/lib/private/AppFramework/Routing/RouteParser.php
index 1b3a6c1255a..1b05c23df9d 100644
--- a/lib/private/AppFramework/Routing/RouteParser.php
+++ b/lib/private/AppFramework/Routing/RouteParser.php
@@ -100,7 +100,13 @@ class RouteParser {
$controllerName = $this->buildControllerName($controller);
$actionName = $this->buildActionName($action);
- $routeName = $routeNamePrefix . $appName . '.' . $controller . '.' . $action . $postfix;
+ /*
+ * 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 . $appName . '.' . $controller . '.' . $action . $postfix);
$routeObject = new Route($url);
$routeObject->method($verb);
diff --git a/lib/private/AppFramework/ScopedPsrLogger.php b/lib/private/AppFramework/ScopedPsrLogger.php
index 4ed91cdb6c0..1cb58da11ef 100644
--- a/lib/private/AppFramework/ScopedPsrLogger.php
+++ b/lib/private/AppFramework/ScopedPsrLogger.php
@@ -37,7 +37,7 @@ class ScopedPsrLogger implements LoggerInterface {
private $appId;
public function __construct(LoggerInterface $inner,
- string $appId) {
+ string $appId) {
$this->inner = $inner;
$this->appId = $appId;
}
diff --git a/lib/private/AppFramework/Services/AppConfig.php b/lib/private/AppFramework/Services/AppConfig.php
index 1fc07bc22b0..1d18baef9ed 100644
--- a/lib/private/AppFramework/Services/AppConfig.php
+++ b/lib/private/AppFramework/Services/AppConfig.php
@@ -26,39 +26,322 @@ declare(strict_types=1);
*/
namespace OC\AppFramework\Services;
+use InvalidArgumentException;
+use JsonException;
use OCP\AppFramework\Services\IAppConfig;
+use OCP\Exceptions\AppConfigTypeConflictException;
+use OCP\Exceptions\AppConfigUnknownKeyException;
use OCP\IConfig;
class AppConfig implements IAppConfig {
- /** @var IConfig */
- private $config;
+ public function __construct(
+ private IConfig $config,
+ /** @var \OC\AppConfig */
+ private \OCP\IAppConfig $appConfig,
+ private string $appName,
+ ) {
+ }
- /** @var string */
- private $appName;
+ /**
+ * @inheritDoc
+ *
+ * @return string[] list of stored config keys
+ * @since 20.0.0
+ */
+ public function getAppKeys(): array {
+ return $this->appConfig->getKeys($this->appName);
+ }
- public function __construct(IConfig $config, string $appName) {
- $this->config = $config;
- $this->appName = $appName;
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config key
+ * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config
+ *
+ * @return bool TRUE if key exists
+ * @since 29.0.0
+ */
+ public function hasAppKey(string $key, ?bool $lazy = false): bool {
+ return $this->appConfig->hasKey($this->appName, $key, $lazy);
}
- public function getAppKeys(): array {
- return $this->config->getAppKeys($this->appName);
+ /**
+ * @param string $key config key
+ * @param bool|null $lazy TRUE to search within lazy loaded config, NULL to search within all config
+ *
+ * @return bool
+ * @throws AppConfigUnknownKeyException if config key is not known
+ * @since 29.0.0
+ */
+ public function isSensitive(string $key, ?bool $lazy = false): bool {
+ return $this->appConfig->isSensitive($this->appName, $key, $lazy);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config key
+ *
+ * @return bool TRUE if config is lazy loaded
+ * @throws AppConfigUnknownKeyException if config key is not known
+ * @see \OCP\IAppConfig for details about lazy loading
+ * @since 29.0.0
+ */
+ public function isLazy(string $key): bool {
+ return $this->appConfig->isLazy($this->appName, $key);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config keys prefix to search
+ * @param bool $filtered TRUE to hide sensitive config values. Value are replaced by {@see IConfig::SENSITIVE_VALUE}
+ *
+ * @return array<string, string> [configKey => configValue]
+ * @since 29.0.0
+ */
+ public function getAllAppValues(string $key = '', bool $filtered = false): array {
+ return $this->appConfig->getAllValues($this->appName, $key, $filtered);
}
+ /**
+ * @inheritDoc
+ *
+ * @param string $key the key of the value, under which will be saved
+ * @param string $value the value that should be stored
+ * @since 20.0.0
+ * @deprecated 29.0.0 use {@see setAppValueString()}
+ */
public function setAppValue(string $key, string $value): void {
- $this->config->setAppValue($this->appName, $key, $value);
+ /** @psalm-suppress InternalMethod */
+ $this->appConfig->setValueMixed($this->appName, $key, $value);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config key
+ * @param string $value config value
+ * @param bool $lazy set config as lazy loaded
+ * @param bool $sensitive if TRUE value will be hidden when listing config values.
+ *
+ * @return bool TRUE if value was different, therefor updated in database
+ * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
+ * @since 29.0.0
+ * @see \OCP\IAppConfig for explanation about lazy loading
+ */
+ public function setAppValueString(
+ string $key,
+ string $value,
+ bool $lazy = false,
+ bool $sensitive = false
+ ): bool {
+ return $this->appConfig->setValueString($this->appName, $key, $value, $lazy, $sensitive);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config key
+ * @param int $value config value
+ * @param bool $lazy set config as lazy loaded
+ * @param bool $sensitive if TRUE value will be hidden when listing config values.
+ *
+ * @return bool TRUE if value was different, therefor updated in database
+ * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
+ * @since 29.0.0
+ * @see \OCP\IAppConfig for explanation about lazy loading
+ */
+ public function setAppValueInt(
+ string $key,
+ int $value,
+ bool $lazy = false,
+ bool $sensitive = false
+ ): bool {
+ return $this->appConfig->setValueInt($this->appName, $key, $value, $lazy, $sensitive);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config key
+ * @param float $value config value
+ * @param bool $lazy set config as lazy loaded
+ * @param bool $sensitive if TRUE value will be hidden when listing config values.
+ *
+ * @return bool TRUE if value was different, therefor updated in database
+ * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
+ * @since 29.0.0
+ * @see \OCP\IAppConfig for explanation about lazy loading
+ */
+ public function setAppValueFloat(
+ string $key,
+ float $value,
+ bool $lazy = false,
+ bool $sensitive = false
+ ): bool {
+ return $this->appConfig->setValueFloat($this->appName, $key, $value, $lazy, $sensitive);
}
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config key
+ * @param bool $value config value
+ * @param bool $lazy set config as lazy loaded
+ *
+ * @return bool TRUE if value was different, therefor updated in database
+ * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
+ * @since 29.0.0
+ * @see \OCP\IAppConfig for explanation about lazy loading
+ */
+ public function setAppValueBool(
+ string $key,
+ bool $value,
+ bool $lazy = false
+ ): bool {
+ return $this->appConfig->setValueBool($this->appName, $key, $value, $lazy);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config key
+ * @param array $value config value
+ * @param bool $lazy set config as lazy loaded
+ * @param bool $sensitive if TRUE value will be hidden when listing config values.
+ *
+ * @return bool TRUE if value was different, therefor updated in database
+ * @throws AppConfigTypeConflictException if type from database is not VALUE_MIXED and different from the requested one
+ * @throws JsonException
+ * @since 29.0.0
+ * @see \OCP\IAppConfig for explanation about lazy loading
+ */
+ public function setAppValueArray(
+ string $key,
+ array $value,
+ bool $lazy = false,
+ bool $sensitive = false
+ ): bool {
+ return $this->appConfig->setValueArray($this->appName, $key, $value, $lazy, $sensitive);
+ }
+
+ /**
+ * @param string $key
+ * @param string $default
+ *
+ * @since 20.0.0
+ * @deprecated 29.0.0 use {@see getAppValueString()}
+ * @return string
+ */
public function getAppValue(string $key, string $default = ''): string {
- return $this->config->getAppValue($this->appName, $key, $default);
+ /** @psalm-suppress InternalMethod */
+ /** @psalm-suppress UndefinedInterfaceMethod */
+ return $this->appConfig->getValueMixed($this->appName, $key, $default);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config key
+ * @param string $default default value
+ * @param bool $lazy search within lazy loaded config
+ *
+ * @return string stored config value or $default if not set in database
+ * @throws InvalidArgumentException if one of the argument format is invalid
+ * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
+ * @since 29.0.0
+ * @see \OCP\IAppConfig for explanation about lazy loading
+ */
+ public function getAppValueString(string $key, string $default = '', bool $lazy = false): string {
+ return $this->appConfig->getValueString($this->appName, $key, $default, $lazy);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config key
+ * @param int $default default value
+ * @param bool $lazy search within lazy loaded config
+ *
+ * @return int stored config value or $default if not set in database
+ * @throws InvalidArgumentException if one of the argument format is invalid
+ * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
+ * @since 29.0.0
+ * @see \OCP\IAppConfig for explanation about lazy loading
+ */
+ public function getAppValueInt(string $key, int $default = 0, bool $lazy = false): int {
+ return $this->appConfig->getValueInt($this->appName, $key, $default, $lazy);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config key
+ * @param float $default default value
+ * @param bool $lazy search within lazy loaded config
+ *
+ * @return float stored config value or $default if not set in database
+ * @throws InvalidArgumentException if one of the argument format is invalid
+ * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
+ * @since 29.0.0
+ * @see \OCP\IAppConfig for explanation about lazy loading
+ */
+ public function getAppValueFloat(string $key, float $default = 0, bool $lazy = false): float {
+ return $this->appConfig->getValueFloat($this->appName, $key, $default, $lazy);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config key
+ * @param bool $default default value
+ * @param bool $lazy search within lazy loaded config
+ *
+ * @return bool stored config value or $default if not set in database
+ * @throws InvalidArgumentException if one of the argument format is invalid
+ * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
+ * @since 29.0.0
+ * @see \OCP\IAppConfig for explanation about lazy loading
+ */
+ public function getAppValueBool(string $key, bool $default = false, bool $lazy = false): bool {
+ return $this->appConfig->getValueBool($this->appName, $key, $default, $lazy);
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * @param string $key config key
+ * @param array $default default value
+ * @param bool $lazy search within lazy loaded config
+ *
+ * @return array stored config value or $default if not set in database
+ * @throws InvalidArgumentException if one of the argument format is invalid
+ * @throws AppConfigTypeConflictException in case of conflict with the value type set in database
+ * @since 29.0.0
+ * @see \OCP\IAppConfig for explanation about lazy loading
+ */
+ public function getAppValueArray(string $key, array $default = [], bool $lazy = false): array {
+ return $this->appConfig->getValueArray($this->appName, $key, $default, $lazy);
}
+ /**
+ * @inheritDoc
+ *
+ * @param string $key the key of the value, under which it was saved
+ * @since 20.0.0
+ */
public function deleteAppValue(string $key): void {
- $this->config->deleteAppValue($this->appName, $key);
+ $this->appConfig->deleteKey($this->appName, $key);
}
+ /**
+ * @inheritDoc
+ *
+ * @since 20.0.0
+ */
public function deleteAppValues(): void {
- $this->config->deleteAppValues($this->appName);
+ $this->appConfig->deleteApp($this->appName);
}
public function setUserValue(string $userId, string $key, string $value, ?string $preCondition = null): void {
diff --git a/lib/private/AppFramework/Utility/ControllerMethodReflector.php b/lib/private/AppFramework/Utility/ControllerMethodReflector.php
index b76b3c33c42..bd68dd96ed4 100644
--- a/lib/private/AppFramework/Utility/ControllerMethodReflector.php
+++ b/lib/private/AppFramework/Utility/ControllerMethodReflector.php
@@ -42,6 +42,7 @@ class ControllerMethodReflector implements IControllerMethodReflector {
public $annotations = [];
private $types = [];
private $parameters = [];
+ private array $ranges = [];
/**
* @param object $object an object or classname
@@ -54,26 +55,38 @@ class ControllerMethodReflector implements IControllerMethodReflector {
if ($docs !== false) {
// extract everything prefixed by @ and first letter uppercase
preg_match_all('/^\h+\*\h+@(?P<annotation>[A-Z]\w+)((?P<parameter>.*))?$/m', $docs, $matches);
- foreach ($matches['annotation'] as $key => $annontation) {
- $annontation = strtolower($annontation);
+ foreach ($matches['annotation'] as $key => $annotation) {
+ $annotation = strtolower($annotation);
$annotationValue = $matches['parameter'][$key];
- if (isset($annotationValue[0]) && $annotationValue[0] === '(' && $annotationValue[\strlen($annotationValue) - 1] === ')') {
+ if (str_starts_with($annotationValue, '(') && str_ends_with($annotationValue, ')')) {
$cutString = substr($annotationValue, 1, -1);
$cutString = str_replace(' ', '', $cutString);
- $splittedArray = explode(',', $cutString);
- foreach ($splittedArray as $annotationValues) {
+ $splitArray = explode(',', $cutString);
+ foreach ($splitArray as $annotationValues) {
[$key, $value] = explode('=', $annotationValues);
- $this->annotations[$annontation][$key] = $value;
+ $this->annotations[$annotation][$key] = $value;
}
continue;
}
- $this->annotations[$annontation] = [$annotationValue];
+ $this->annotations[$annotation] = [$annotationValue];
}
// extract type parameter information
preg_match_all('/@param\h+(?P<type>\w+)\h+\$(?P<var>\w+)/', $docs, $matches);
$this->types = array_combine($matches['var'], $matches['type']);
+ preg_match_all('/@psalm-param\h+(?P<type>\w+)<(?P<rangeMin>(-?\d+|min)),\h*(?P<rangeMax>(-?\d+|max))>\h+\$(?P<var>\w+)/', $docs, $matches);
+ foreach ($matches['var'] as $index => $varName) {
+ if ($matches['type'][$index] !== 'int') {
+ // only int ranges are possible at the moment
+ // @see https://psalm.dev/docs/annotating_code/type_syntax/scalar_types
+ continue;
+ }
+ $this->ranges[$varName] = [
+ 'min' => $matches['rangeMin'][$index] === 'min' ? PHP_INT_MIN : (int)$matches['rangeMin'][$index],
+ 'max' => $matches['rangeMax'][$index] === 'max' ? PHP_INT_MAX : (int)$matches['rangeMax'][$index],
+ ];
+ }
}
foreach ($reflection->getParameters() as $param) {
@@ -106,6 +119,14 @@ class ControllerMethodReflector implements IControllerMethodReflector {
return null;
}
+ public function getRange(string $parameter): ?array {
+ if (array_key_exists($parameter, $this->ranges)) {
+ return $this->ranges[$parameter];
+ }
+
+ return null;
+ }
+
/**
* @return array the arguments of the method with key => default value
*/
diff --git a/lib/private/AppFramework/Utility/SimpleContainer.php b/lib/private/AppFramework/Utility/SimpleContainer.php
index 7aa5cb83926..83aed4381b3 100644
--- a/lib/private/AppFramework/Utility/SimpleContainer.php
+++ b/lib/private/AppFramework/Utility/SimpleContainer.php
@@ -37,8 +37,8 @@ use Pimple\Container;
use Psr\Container\ContainerInterface;
use ReflectionClass;
use ReflectionException;
-use ReflectionParameter;
use ReflectionNamedType;
+use ReflectionParameter;
use function class_exists;
/**
@@ -105,6 +105,11 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
try {
return $this->query($resolveName);
} catch (QueryException $e2) {
+ // Pass null if typed and nullable
+ if ($parameter->allowsNull() && ($parameterType instanceof ReflectionNamedType)) {
+ return null;
+ }
+
// don't lose the error we got while trying to query by type
throw new QueryException($e->getMessage(), (int) $e->getCode(), $e);
}
diff --git a/lib/private/AppFramework/Utility/TimeFactory.php b/lib/private/AppFramework/Utility/TimeFactory.php
index 1e4655dd1cd..737777a11ac 100644
--- a/lib/private/AppFramework/Utility/TimeFactory.php
+++ b/lib/private/AppFramework/Utility/TimeFactory.php
@@ -34,7 +34,7 @@ use OCP\AppFramework\Utility\ITimeFactory;
* Use this to get a timestamp or DateTime object in code to remain testable
*
* @since 8.0.0
- * @since 26.0.0 Extends the \Psr\Clock\ClockInterface interface
+ * @since 27.0.0 Implements the \Psr\Clock\ClockInterface interface
* @ref https://www.php-fig.org/psr/psr-20/#21-clockinterface
*/
class TimeFactory implements ITimeFactory {
@@ -73,4 +73,11 @@ class TimeFactory implements ITimeFactory {
return $clone;
}
+
+ public function getTimeZone(?string $timezone = null): \DateTimeZone {
+ if ($timezone !== null) {
+ return new \DateTimeZone($timezone);
+ }
+ return $this->timezone;
+ }
}