Compare commits

...

1 Commits

Author SHA1 Message Date
Carl Schwan 27d7c18b77 feat(AppFramework): Add support for PSR-7 ServerRequestInterface
Signed-off-by: Carl Schwan <carlschwan@kde.org>
2026-02-25 15:07:32 +01:00
25 changed files with 126 additions and 91 deletions
@@ -19,9 +19,9 @@ use OCP\AppFramework\OCS\OCSForbiddenException;
use OCP\AppFramework\OCSController;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\IRequest;
use OCP\Security\Bruteforce\IThrottler;
use OCP\Security\ISecureRandom;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
/**
@@ -35,7 +35,7 @@ use Psr\Log\LoggerInterface;
class OCSAuthAPIController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
ServerRequestInterface $request,
private ISecureRandom $secureRandom,
private IJobList $jobList,
private TrustedServers $trustedServers,
+21 -10
View File
@@ -48,13 +48,12 @@ use OCP\Files\IRootFolder;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IPreview;
use OCP\IRequest;
use OCP\IServerContainer;
use OCP\ITagManager;
use OCP\IUserSession;
use OCP\Share\IManager as IShareManager;
use OCP\Util;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
class Application extends App implements IBootstrap {
@@ -69,18 +68,24 @@ class Application extends App implements IBootstrap {
* Controllers
*/
$context->registerService('APIController', function (ContainerInterface $c) {
/** @var IServerContainer $server */
$server = $c->get(IServerContainer::class);
$user = $c->get(IUserSession::class)->getUser();
if (!$user) {
$folder = null;
} else {
$userId = $user->getUID();
$root = $c->get(IRootFolder::class);
$folder = $root->getUserFolder($userId);
}
return new ApiController(
$c->get('AppName'),
$c->get(IRequest::class),
$c->get('appName'),
$c->get(ServerRequestInterface::class),
$c->get(IUserSession::class),
$c->get(TagService::class),
$c->get(IPreview::class),
$c->get(IShareManager::class),
$c->get(IConfig::class),
$server->getUserFolder(),
$folder,
$c->get(UserConfig::class),
$c->get(ViewConfig::class),
$c->get(IL10N::class),
@@ -93,14 +98,20 @@ class Application extends App implements IBootstrap {
* Services
*/
$context->registerService(TagService::class, function (ContainerInterface $c) {
/** @var IServerContainer $server */
$server = $c->get(IServerContainer::class);
$user = $c->get(IUserSession::class)->getUser();
if (!$user) {
$folder = null;
} else {
$userId = $user->getUID();
$root = $c->get(IRootFolder::class);
$folder = $root->getUserFolder($userId);
}
return new TagService(
$c->get(IUserSession::class),
$c->get(IActivityManager::class),
$c->get(ITagManager::class)->load(self::APP_ID),
$server->getUserFolder(),
$folder,
);
});
+2 -2
View File
@@ -38,12 +38,12 @@ use OCP\Files\StorageNotAvailableException;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IPreview;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserSession;
use OCP\PreConditionNotMetException;
use OCP\Share\IManager;
use OCP\Share\IShare;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
use Throwable;
@@ -55,7 +55,7 @@ use Throwable;
class ApiController extends Controller {
public function __construct(
string $appName,
IRequest $request,
ServerRequestInterface $request,
private IUserSession $userSession,
private TagService $tagService,
private IPreview $previewManager,
@@ -26,13 +26,13 @@ use OCP\Files\File;
use OCP\Files\GenericFileException;
use OCP\Files\IRootFolder;
use OCP\IL10N;
use OCP\IRequest;
use Psr\Http\Message\ServerRequestInterface;
use function OCP\Log\logger;
class ConversionApiController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
ServerRequestInterface $request,
private IConversionManager $fileConversionManager,
private IRootFolder $rootFolder,
private IL10N $l10n,
@@ -15,14 +15,14 @@ use OCP\AppFramework\OCSController;
use OCP\DirectEditing\IManager;
use OCP\DirectEditing\RegisterDirectEditorEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IRequest;
use OCP\IURLGenerator;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
class DirectEditingController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
ServerRequestInterface $request,
string $corsMethods,
string $corsAllowedHeaders,
int $corsMaxAge,
@@ -17,14 +17,14 @@ use OCP\AppFramework\Http\Response;
use OCP\DirectEditing\IManager;
use OCP\DirectEditing\RegisterDirectEditorEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IRequest;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
class DirectEditingViewController extends Controller {
public function __construct(
$appName,
IRequest $request,
string $appName,
ServerRequestInterface $request,
private IEventDispatcher $eventDispatcher,
private IManager $directEditingManager,
private LoggerInterface $logger,
@@ -15,21 +15,17 @@ use OCP\AppFramework\Http\Attribute\Route;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSBadRequestException;
use OCP\AppFramework\OCSController;
use OCP\AppFramework\Services\IAppConfig;
use OCP\BackgroundJob\IJobList;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUserManager;
use Psr\Http\Message\ServerRequestInterface;
class FilenamesController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
ServerRequestInterface $request,
private IL10N $l10n,
private IJobList $jobList,
private IAppConfig $appConfig,
private IUserManager $userManager,
private SettingsService $settingsService,
) {
parent::__construct($appName, $request);
@@ -20,8 +20,8 @@ use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\Exception;
use OCP\IRequest;
use OCP\Security\ISecureRandom;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
class OpenLocalEditorController extends OCSController {
@@ -31,7 +31,7 @@ class OpenLocalEditorController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
ServerRequestInterface $request,
protected ITimeFactory $timeFactory,
protected OpenLocalEditorMapper $mapper,
protected ISecureRandom $secureRandom,
@@ -18,7 +18,7 @@ use OCP\Files\GenericFileException;
use OCP\Files\Template\ITemplateManager;
use OCP\Files\Template\Template;
use OCP\Files\Template\TemplateFileCreator;
use OCP\IRequest;
use Psr\Http\Message\ServerRequestInterface;
/**
* @psalm-import-type FilesTemplateFile from ResponseDefinitions
@@ -30,7 +30,7 @@ use OCP\IRequest;
class TemplateController extends OCSController {
public function __construct(
$appName,
IRequest $request,
ServerRequestInterface $request,
protected ITemplateManager $templateManager,
) {
parent::__construct($appName, $request);
@@ -20,15 +20,15 @@ use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\Files\IHomeStorage;
use OCP\Files\IRootFolder;
use OCP\IRequest;
use OCP\IUserManager;
use OCP\Notification\IManager as NotificationManager;
use Psr\Http\Message\ServerRequestInterface;
class TransferOwnershipController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
ServerRequestInterface $request,
private string $userId,
private NotificationManager $notificationManager,
private ITimeFactory $timeFactory,
+2 -4
View File
@@ -34,11 +34,10 @@ use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\Template\ITemplateManager;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUserSession;
use OCP\Util;
use Psr\Http\Message\ServerRequestInterface;
/**
* @package OCA\Files\Controller
@@ -48,9 +47,8 @@ class ViewController extends Controller {
public function __construct(
string $appName,
IRequest $request,
ServerRequestInterface $request,
private IURLGenerator $urlGenerator,
private IL10N $l10n,
private IConfig $config,
private IEventDispatcher $eventDispatcher,
private IUserSession $userSession,
@@ -28,7 +28,6 @@ use OCP\Files\IRootFolder;
use OCP\Files\Template\ITemplateManager;
use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUser;
@@ -53,7 +52,6 @@ class ViewControllerTest extends TestCase {
private IEventDispatcher $eventDispatcher;
private IEventLogger&MockObject $eventLogger;
private IInitialState&MockObject $initialState;
private IL10N&MockObject $l10n;
private IRequest&MockObject $request;
private IRootFolder&MockObject $rootFolder;
private ITemplateManager&MockObject $templateManager;
@@ -74,7 +72,6 @@ class ViewControllerTest extends TestCase {
$this->config = $this->createMock(IConfig::class);
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
$this->initialState = $this->createMock(IInitialState::class);
$this->l10n = $this->createMock(IL10N::class);
$this->request = $this->createMock(IRequest::class);
$this->rootFolder = $this->createMock(IRootFolder::class);
$this->templateManager = $this->createMock(ITemplateManager::class);
@@ -130,7 +127,6 @@ class ViewControllerTest extends TestCase {
'files',
$this->request,
$this->urlGenerator,
$this->l10n,
$this->config,
$this->eventDispatcher,
$this->userSession,
+5 -4
View File
@@ -13,11 +13,12 @@ use OCA\Theming\ITheme;
use OCA\Theming\ThemingDefaults;
use OCA\Theming\Util;
use OCP\App\IAppManager;
use OCP\AppFramework\RequestHelper;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUserSession;
use Psr\Http\Message\ServerRequestInterface;
class DefaultTheme implements ITheme {
use CommonThemeTrait;
@@ -34,7 +35,7 @@ class DefaultTheme implements ITheme {
public IConfig $config,
public IL10N $l,
public IAppManager $appManager,
private ?IRequest $request,
private ?ServerRequestInterface $request,
) {
$this->defaultPrimaryColor = $this->themingDefaults->getDefaultColorPrimary();
$this->primaryColor = $this->themingDefaults->getColorPrimary();
@@ -97,9 +98,9 @@ class DefaultTheme implements ITheme {
$user = $this->userSession->getUser();
// Chromium based browsers currently (2024) have huge performance issues with blur filters
$isChromium = $this->request !== null && $this->request->isUserAgent([Request::USER_AGENT_CHROME, Request::USER_AGENT_MS_EDGE]);
$isChromium = $this->request !== null && RequestHelper::isUserAgent($this->request, [Request::USER_AGENT_CHROME, Request::USER_AGENT_MS_EDGE]);
// Ignore MacOS because they always have hardware accelartion
$isChromium = $isChromium && !$this->request->isUserAgent(['/Macintosh/']);
$isChromium = $isChromium && !RequestHelper::isUserAgent($this->request, ['/Macintosh/']);
// Allow to force the blur filter
$forceEnableBlur = $user === null ? false : $this->config->getUserValue(
$user->getUID(),
@@ -26,8 +26,8 @@ use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCS\OCSForbiddenException;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
use OCP\ISession;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
/**
@@ -37,7 +37,7 @@ use Psr\Log\LoggerInterface;
class WebhooksController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
ServerRequestInterface $request,
private LoggerInterface $logger,
private WebhookListenerMapper $mapper,
private ?string $userId,
@@ -143,7 +143,8 @@ class WebhooksController extends OCSController {
): DataResponse {
$appId = null;
if ($this->session->get('app_api') === true) {
$appId = $this->request->getHeader('ex-app-id');
$appIds = $this->request->getHeader('ex-app-id');
$appId = $appIds !== [] ? $appIds[0] : null;
}
try {
$authMethod = AuthMethod::from($authMethod ?? AuthMethod::None->value);
@@ -219,7 +220,8 @@ class WebhooksController extends OCSController {
): DataResponse {
$appId = null;
if ($this->session->get('app_api') === true) {
$appId = $this->request->getHeader('ex-app-id');
$appIds = $this->request->getHeader('ex-app-id');
$appId = $appIds !== [] ? $appIds[0] : null;
}
try {
$authMethod = AuthMethod::from($authMethod ?? AuthMethod::None->value);
-6
View File
@@ -1227,14 +1227,8 @@
</TypeDoesNotContainType>
</file>
<file src="apps/files/lib/AppInfo/Application.php">
<DeprecatedInterface>
<code><![CDATA[$server]]></code>
<code><![CDATA[$server]]></code>
</DeprecatedInterface>
<DeprecatedMethod>
<code><![CDATA[Util::connectHook('\OCP\Config', 'js', '\OCA\Files\App', 'extendJsConfig')]]></code>
<code><![CDATA[getUserFolder]]></code>
<code><![CDATA[getUserFolder]]></code>
</DeprecatedMethod>
</file>
<file src="apps/files/lib/BackgroundJob/ScanFiles.php">
@@ -69,6 +69,7 @@ class ClientFlowLoginController extends Controller {
}
private function getClientName(): string {
/** @var string $userAgent */
$userAgent = $this->request->getHeader('user-agent');
return $userAgent !== '' ? $userAgent : 'unknown';
}
+16
View File
@@ -8,6 +8,8 @@
namespace OC;
use bantu\IniGetWrapper\IniGetWrapper;
use Nyholm\Psr7\Factory\Psr17Factory;
use Nyholm\Psr7Server\ServerRequestCreator;
use OC\Accounts\AccountManager;
use OC\App\AppManager;
use OC\App\AppStore\Bundles\BundleFetcher;
@@ -280,6 +282,7 @@ use OCP\User\Events\UserLoggedInWithCookieEvent;
use OCP\User\Events\UserLoggedOutEvent;
use OCP\User\IAvailabilityCoordinator;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerInterface;
/**
@@ -880,6 +883,19 @@ class Server extends ServerContainer implements IServerContainer {
$c->get(IMimeTypeDetector::class)
);
});
$this->registerService(ServerRequestInterface::class, function (): ServerRequestInterface {
$psr17Factory = new Psr17Factory();
$creator = new ServerRequestCreator(
$psr17Factory, // ServerRequestFactory
$psr17Factory, // UriFactory
$psr17Factory, // UploadedFileFactory
$psr17Factory // StreamFactory
);
$requestId = $this->get(IRequestId::class);
return $creator->fromGlobals()
->withAttribute('request-id', $requestId->getId());
});
$this->registerService(Request::class, function (ContainerInterface $c) {
if (isset($this['urlParams'])) {
$urlParams = $this['urlParams'];
+3 -2
View File
@@ -12,6 +12,7 @@ use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Response;
use OCP\IRequest;
use Psr\Http\Message\ServerRequestInterface;
/**
* Base class to inherit your controllers from that are used for RESTful APIs
@@ -25,7 +26,7 @@ abstract class ApiController extends Controller {
/**
* constructor of the controller
* @param string $appName the name of the app
* @param IRequest $request an instance of the request
* @param IRequest|ServerRequestInterface $request an instance of the request
* @param string $corsMethods comma separated string of HTTP verbs which
* should be allowed for websites or webapps when calling your API, defaults to
* 'PUT, POST, GET, DELETE, PATCH'
@@ -37,7 +38,7 @@ abstract class ApiController extends Controller {
* @since 7.0.0
*/
public function __construct($appName,
IRequest $request,
IRequest|ServerRequestInterface $request,
$corsMethods = 'PUT, POST, GET, DELETE, PATCH',
$corsAllowedHeaders = 'Authorization, Content-Type, Accept',
$corsMaxAge = 1728000) {
@@ -17,6 +17,7 @@ use OCP\AppFramework\Http\TemplateResponse;
use OCP\IRequest;
use OCP\ISession;
use OCP\IURLGenerator;
use Psr\Http\Message\ServerRequestInterface;
/**
* Base controller for interactive public shares
@@ -37,7 +38,7 @@ abstract class AuthPublicShareController extends PublicShareController {
* @since 14.0.0
*/
public function __construct(string $appName,
IRequest $request,
IRequest|ServerRequestInterface $request,
ISession $session,
IURLGenerator $urlGenerator) {
parent::__construct($appName, $request, $session);
+7 -21
View File
@@ -12,26 +12,13 @@ use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Response;
use OCP\IRequest;
use Psr\Http\Message\ServerRequestInterface;
/**
* Base class to inherit your controllers from
* @since 6.0.0
*/
abstract class Controller {
/**
* app name
* @var string
* @since 7.0.0
*/
protected $appName;
/**
* current request
* @var \OCP\IRequest
* @since 6.0.0
*/
protected $request;
/**
* @var array<string, Closure>
* @since 7.0.0
@@ -39,16 +26,15 @@ abstract class Controller {
private $responders;
/**
* constructor of the controller
* Constructor of the controller
* @param string $appName the name of the app
* @param IRequest $request an instance of the request
* @param IRequest|ServerRequestInterface $request an instance of the request
* @since 6.0.0 - parameter $appName was added in 7.0.0 - parameter $app was removed in 7.0.0
*/
public function __construct($appName,
IRequest $request) {
$this->appName = $appName;
$this->request = $request;
public function __construct(
protected $appName,
protected $request,
) {
// default responders
$this->responders = [
'json' => function ($data) {
+3 -7
View File
@@ -10,7 +10,7 @@ namespace OCP\AppFramework\Http;
use OCP\AppFramework\Http;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IConfig;
use OCP\IRequest;
use OCP\IRequestId;
use OCP\IUserSession;
use OCP\Server;
use Psr\Log\LoggerInterface;
@@ -226,13 +226,9 @@ class Response {
* @since 6.0.0
*/
public function getHeaders() {
/** @var IRequest $request */
/**
* @psalm-suppress UndefinedClass
*/
$request = Server::get(IRequest::class);
$requestId = Server::get(IRequestId::class);
$mergeWith = [
'X-Request-Id' => $request->getId(),
'X-Request-Id' => $requestId->getId(),
'Cache-Control' => 'no-cache, no-store, must-revalidate',
'Content-Security-Policy' => $this->getContentSecurityPolicy()->buildPolicy(),
'Feature-Policy' => $this->getFeaturePolicy()->buildPolicy(),
+3 -2
View File
@@ -11,6 +11,7 @@ use OC\Streamer;
use OCP\AppFramework\Http;
use OCP\IDateTimeZone;
use OCP\IRequest;
use Psr\Http\Message\ServerRequestInterface;
/**
* Public library to send several files in one zip archive.
@@ -25,14 +26,14 @@ class ZipResponse extends Response implements ICallbackResponse {
private array $resources = [];
/** @var string Filename that the zip file should have */
private string $name;
private IRequest $request;
private ServerRequestInterface|IRequest $request;
/**
* @param S $status
* @param H $headers
* @since 15.0.0
*/
public function __construct(IRequest $request, string $name = 'output', int $status = Http::STATUS_OK, array $headers = []) {
public function __construct(ServerRequestInterface|IRequest $request, string $name = 'output', int $status = Http::STATUS_OK, array $headers = []) {
parent::__construct($status, $headers);
$this->name = $name;
+3 -2
View File
@@ -10,6 +10,7 @@ namespace OCP\AppFramework;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\Response;
use OCP\IRequest;
use Psr\Http\Message\ServerRequestInterface;
/**
* Base class to inherit your controllers from that are used for RESTful APIs
@@ -42,7 +43,7 @@ abstract class OCSController extends ApiController {
/**
* constructor of the controller
* @param string $appName the name of the app
* @param IRequest $request an instance of the request
* @param ServerRequestInterface|IRequest $request an instance of the request
* @param string $corsMethods comma separated string of HTTP verbs which
* should be allowed for websites or webapps when calling your API, defaults to
* 'PUT, POST, GET, DELETE, PATCH'
@@ -54,7 +55,7 @@ abstract class OCSController extends ApiController {
* @since 8.1.0
*/
public function __construct($appName,
IRequest $request,
ServerRequestInterface|IRequest $request,
$corsMethods = 'PUT, POST, GET, DELETE, PATCH',
$corsAllowedHeaders = 'Authorization, Content-Type, Accept, OCS-APIRequest',
$corsMaxAge = 1728000) {
@@ -9,6 +9,7 @@ namespace OCP\AppFramework;
use OCP\IRequest;
use OCP\ISession;
use Psr\Http\Message\ServerRequestInterface;
/**
* Base controller for public shares
@@ -36,7 +37,7 @@ abstract class PublicShareController extends Controller {
*/
public function __construct(
string $appName,
IRequest $request,
IRequest|ServerRequestInterface $request,
protected ISession $session,
) {
parent::__construct($appName, $request);
+33
View File
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH
* SPDX-FileContributor: Carl Schwan
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCP\AppFramework;
use Psr\Http\Message\MessageInterface;
/**
* A collection of helper methods to manipulate RequestInterface
*
* @since 34.0.0
*/
final class RequestHelper {
public static function isUserAgent(MessageInterface $message, array $agent): bool {
$userAgent = $message->getHeader('HTTP_USER_AGENT');
if ($userAgent === []) {
return false;
}
foreach ($agent as $regex) {
if (preg_match($regex, $userAgent[0])) {
return true;
}
}
return false;
}
}