mirror of
https://github.com/nextcloud/server.git
synced 2026-06-10 09:13:19 -04:00
feat(AppFramework): Add ExAppRequired attribute
Signed-off-by: provokateurin <kate@provokateurin.de>
This commit is contained in:
parent
beececf660
commit
5aefdc399e
7 changed files with 110 additions and 2 deletions
|
|
@ -47,6 +47,7 @@ return array(
|
|||
'OCP\\AppFramework\\Http\\Attribute\\AuthorizedAdminSetting' => $baseDir . '/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php',
|
||||
'OCP\\AppFramework\\Http\\Attribute\\BruteForceProtection' => $baseDir . '/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php',
|
||||
'OCP\\AppFramework\\Http\\Attribute\\CORS' => $baseDir . '/lib/public/AppFramework/Http/Attribute/CORS.php',
|
||||
'OCP\\AppFramework\\Http\\Attribute\\ExAppRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/ExAppRequired.php',
|
||||
'OCP\\AppFramework\\Http\\Attribute\\FrontpageRoute' => $baseDir . '/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php',
|
||||
'OCP\\AppFramework\\Http\\Attribute\\IgnoreOpenAPI' => $baseDir . '/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php',
|
||||
'OCP\\AppFramework\\Http\\Attribute\\NoAdminRequired' => $baseDir . '/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php',
|
||||
|
|
@ -887,6 +888,7 @@ return array(
|
|||
'OC\\AppFramework\\Middleware\\Security\\CSPMiddleware' => $baseDir . '/lib/private/AppFramework/Middleware/Security/CSPMiddleware.php',
|
||||
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\AppNotEnabledException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php',
|
||||
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\CrossSiteRequestForgeryException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/CrossSiteRequestForgeryException.php',
|
||||
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\ExAppRequiredException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/ExAppRequiredException.php',
|
||||
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\LaxSameSiteCookieFailedException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php',
|
||||
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotAdminException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotAdminException.php',
|
||||
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotConfirmedException' => $baseDir . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php',
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OCP\\AppFramework\\Http\\Attribute\\AuthorizedAdminSetting' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php',
|
||||
'OCP\\AppFramework\\Http\\Attribute\\BruteForceProtection' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php',
|
||||
'OCP\\AppFramework\\Http\\Attribute\\CORS' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/CORS.php',
|
||||
'OCP\\AppFramework\\Http\\Attribute\\ExAppRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/ExAppRequired.php',
|
||||
'OCP\\AppFramework\\Http\\Attribute\\FrontpageRoute' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php',
|
||||
'OCP\\AppFramework\\Http\\Attribute\\IgnoreOpenAPI' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php',
|
||||
'OCP\\AppFramework\\Http\\Attribute\\NoAdminRequired' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php',
|
||||
|
|
@ -920,6 +921,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\AppFramework\\Middleware\\Security\\CSPMiddleware' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/CSPMiddleware.php',
|
||||
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\AppNotEnabledException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php',
|
||||
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\CrossSiteRequestForgeryException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/CrossSiteRequestForgeryException.php',
|
||||
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\ExAppRequiredException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/ExAppRequiredException.php',
|
||||
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\LaxSameSiteCookieFailedException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php',
|
||||
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotAdminException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotAdminException.php',
|
||||
'OC\\AppFramework\\Middleware\\Security\\Exceptions\\NotConfirmedException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OC\AppFramework\Middleware\Security\Exceptions;
|
||||
|
||||
use OCP\AppFramework\Http;
|
||||
|
||||
/**
|
||||
* Class ExAppRequiredException is thrown when an endpoint can only be called by an ExApp but the caller is not an ExApp.
|
||||
*/
|
||||
class ExAppRequiredException extends SecurityException {
|
||||
public function __construct() {
|
||||
parent::__construct('ExApp required', Http::STATUS_PRECONDITION_FAILED);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,16 +10,19 @@ namespace OC\AppFramework\Middleware\Security;
|
|||
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\AppNotEnabledException;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\ExAppRequiredException;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\SecurityException;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException;
|
||||
use OC\AppFramework\Utility\ControllerMethodReflector;
|
||||
use OC\Settings\AuthorizedGroupMapper;
|
||||
use OC\User\Session;
|
||||
use OCP\App\AppPathNotFoundException;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
|
||||
use OCP\AppFramework\Http\Attribute\ExAppRequired;
|
||||
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||
use OCP\AppFramework\Http\Attribute\PublicPage;
|
||||
|
|
@ -127,7 +130,12 @@ class SecurityMiddleware extends Middleware {
|
|||
|
||||
// security checks
|
||||
$isPublicPage = $this->hasAnnotationOrAttribute($reflectionMethod, 'PublicPage', PublicPage::class);
|
||||
if (!$isPublicPage) {
|
||||
|
||||
if ($this->hasAnnotationOrAttribute($reflectionMethod, 'ExAppRequired', ExAppRequired::class)) {
|
||||
if (!$this->userSession instanceof Session || $this->userSession->getSession()->get('app_api') !== true) {
|
||||
throw new ExAppRequiredException();
|
||||
}
|
||||
} elseif (!$isPublicPage) {
|
||||
if (!$this->isLoggedIn) {
|
||||
throw new NotLoggedInException();
|
||||
}
|
||||
|
|
|
|||
21
lib/public/AppFramework/Http/Attribute/ExAppRequired.php
Normal file
21
lib/public/AppFramework/Http/Attribute/ExAppRequired.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCP\AppFramework\Http\Attribute;
|
||||
|
||||
use Attribute;
|
||||
|
||||
/**
|
||||
* Attribute for controller methods that can only be accessed by ExApps
|
||||
*
|
||||
* @since 30.0.0
|
||||
*/
|
||||
#[Attribute]
|
||||
class ExAppRequired {
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Test\AppFramework\Middleware\Security\Mock;
|
||||
|
||||
use OCP\AppFramework\Http\Attribute\ExAppRequired;
|
||||
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||
use OCP\AppFramework\Http\Attribute\PublicPage;
|
||||
|
|
@ -156,4 +157,14 @@ class SecurityMiddlewareController extends \OCP\AppFramework\Controller {
|
|||
#[PublicPage]
|
||||
public function testAttributeNoAdminRequiredNoCSRFRequiredPublicPage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @ExAppRequired
|
||||
*/
|
||||
public function testAnnotationExAppRequired() {
|
||||
}
|
||||
|
||||
#[ExAppRequired]
|
||||
public function testAttributeExAppRequired() {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use OC\AppFramework\Http;
|
|||
use OC\AppFramework\Http\Request;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\AppNotEnabledException;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\ExAppRequiredException;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
|
||||
use OC\AppFramework\Middleware\Security\Exceptions\SecurityException;
|
||||
|
|
@ -18,6 +19,7 @@ use OC\Appframework\Middleware\Security\Exceptions\StrictCookieMissingException;
|
|||
use OC\AppFramework\Middleware\Security\SecurityMiddleware;
|
||||
use OC\AppFramework\Utility\ControllerMethodReflector;
|
||||
use OC\Settings\AuthorizedGroupMapper;
|
||||
use OC\User\Session;
|
||||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\AppFramework\Http\RedirectResponse;
|
||||
|
|
@ -27,6 +29,7 @@ use OCP\IL10N;
|
|||
use OCP\INavigationManager;
|
||||
use OCP\IRequest;
|
||||
use OCP\IRequestId;
|
||||
use OCP\ISession;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUserSession;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
|
@ -66,7 +69,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
|||
parent::setUp();
|
||||
|
||||
$this->authorizedGroupMapper = $this->createMock(AuthorizedGroupMapper::class);
|
||||
$this->userSession = $this->createMock(IUserSession::class);
|
||||
$this->userSession = $this->createMock(Session::class);
|
||||
$this->request = $this->createMock(IRequest::class);
|
||||
$this->controller = new SecurityMiddlewareController(
|
||||
'test',
|
||||
|
|
@ -167,6 +170,13 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
|||
];
|
||||
}
|
||||
|
||||
public static function dataExAppRequired(): array {
|
||||
return [
|
||||
['testAnnotationExAppRequired'],
|
||||
['testAttributeExAppRequired'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataNoCSRFRequiredPublicPage
|
||||
*/
|
||||
|
|
@ -682,4 +692,40 @@ class SecurityMiddlewareTest extends \Test\TestCase {
|
|||
|
||||
$this->assertTrue($response instanceof JSONResponse);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataExAppRequired
|
||||
*/
|
||||
public function testExAppRequired(string $method): void {
|
||||
$middleware = $this->getMiddleware(true, false, false);
|
||||
$this->reader->reflect($this->controller, $method);
|
||||
|
||||
$session = $this->createMock(ISession::class);
|
||||
$session->method('get')->with('app_api')->willReturn(true);
|
||||
$this->userSession->method('getSession')->willReturn($session);
|
||||
|
||||
$this->request->expects($this->once())
|
||||
->method('passesStrictCookieCheck')
|
||||
->willReturn(true);
|
||||
$this->request->expects($this->once())
|
||||
->method('passesCSRFCheck')
|
||||
->willReturn(true);
|
||||
|
||||
$middleware->beforeController($this->controller, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataExAppRequired
|
||||
*/
|
||||
public function testExAppRequiredError(string $method): void {
|
||||
$middleware = $this->getMiddleware(true, false, false, false);
|
||||
$this->reader->reflect($this->controller, $method);
|
||||
|
||||
$session = $this->createMock(ISession::class);
|
||||
$session->method('get')->with('app_api')->willReturn(false);
|
||||
$this->userSession->method('getSession')->willReturn($session);
|
||||
|
||||
$this->expectException(ExAppRequiredException::class);
|
||||
$middleware->beforeController($this->controller, $method);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue