Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions core/Controller/PreviewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
namespace OC\Core\Controller;

use OCA\Files_Sharing\SharedStorage;
use OCP\AppFramework\Controller;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
Expand All @@ -39,7 +39,7 @@
use OCP\IPreview;
use OCP\IRequest;

class PreviewController extends Controller {
class PreviewController extends ApiController {
private ?string $userId;
private IRootFolder $root;
private IPreview $preview;
Expand Down Expand Up @@ -87,6 +87,7 @@ public function getPreview(
/**
* @NoAdminRequired
* @NoCSRFRequired
* @CORS
*
* @return DataResponse|FileDisplayResponse
*/
Expand Down
1 change: 1 addition & 0 deletions core/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
['name' => 'TwoFactorChallenge#setupProvider', 'url' => 'login/setupchallenge/{providerId}', 'verb' => 'GET'],
['name' => 'TwoFactorChallenge#confirmProviderSetup', 'url' => 'login/setupchallenge/{providerId}', 'verb' => 'POST'],
['name' => 'OCJS#getConfig', 'url' => '/core/js/oc.js', 'verb' => 'GET'],
['name' => 'Preview#preflighted_cors', 'url' => '/core/preview', 'verb' => 'OPTIONS'],
['name' => 'Preview#getPreviewByFileId', 'url' => '/core/preview', 'verb' => 'GET'],
['name' => 'Preview#getPreview', 'url' => '/core/preview.png', 'verb' => 'GET'],
['name' => 'RecommendedApps#index', 'url' => '/core/apps/recommended', 'verb' => 'GET'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ public function __construct(string $appName, array $urlParams = [], ServerContai
$c->get(IRequest::class),
$c->get(IControllerMethodReflector::class),
$c->get(IUserSession::class),
$c->get(OC\Security\Bruteforce\Throttler::class)
$c->get(OC\Security\Bruteforce\Throttler::class),
$c->get(IConfig::class),
$c->get('AppName')
)
);
$dispatcher->registerMiddleware(
Expand Down
57 changes: 50 additions & 7 deletions lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Middleware;
use OCP\IConfig;
use OCP\IRequest;
use ReflectionMethod;

Expand All @@ -48,29 +49,38 @@
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
*/
class CORSMiddleware extends Middleware {
/** @var IRequest */
/** @var IRequest */
private $request;
/** @var ControllerMethodReflector */
private $reflector;
/** @var Session */
private $session;
/** @var Throttler */
private $throttler;
/** @var IConfig */
private $config;
/** @var string */
private $appName;

/**
* @param IRequest $request
* @param ControllerMethodReflector $reflector
* @param Session $session
* @param Throttler $throttler
* @param string $app_name
*/
public function __construct(IRequest $request,
ControllerMethodReflector $reflector,
Session $session,
Throttler $throttler) {
ControllerMethodReflector $reflector,
Session $session,
Throttler $throttler,
IConfig $config,
$app_name) {
$this->request = $request;
$this->reflector = $reflector;
$this->session = $session;
$this->throttler = $throttler;
$this->config = $config;
$this->appName = $app_name;
}

/**
Expand All @@ -88,8 +98,10 @@ public function beforeController($controller, $methodName) {

// ensure that @CORS annotated API routes are not used in conjunction
// with session authentication since this enables CSRF attack vectors
// also Do nothing if HTTP_ORIGIN is not set
if ($this->hasAnnotationOrAttribute($reflectionMethod, 'CORS', CORS::class) &&
(!$this->hasAnnotationOrAttribute($reflectionMethod, 'PublicPage', PublicPage::class) || $this->session->isLoggedIn())) {
(!$this->hasAnnotationOrAttribute($reflectionMethod, 'PublicPage', PublicPage::class) || $this->session->isLoggedIn()) &&
&& isset($this->request->server['HTTP_ORIGIN'])) {
$user = array_key_exists('PHP_AUTH_USER', $this->request->server) ? $this->request->server['PHP_AUTH_USER'] : null;
$pass = array_key_exists('PHP_AUTH_PW', $this->request->server) ? $this->request->server['PHP_AUTH_PW'] : null;

Expand Down Expand Up @@ -157,15 +169,16 @@ public function afterController($controller, $methodName, Response $response) {
}
}

$origin = $this->request->server['HTTP_ORIGIN'];
$origin = $this->request->server['HTTP_ORIGIN'];
if ($this->isOriginAllowed($origin, $this->appName)) {
$response->addHeader('Access-Control-Allow-Origin', $origin);
}
}
return $response;
}

/**
* If an SecurityException is being caught return a JSON error response
* If a SecurityException is being caught return a JSON error response
*
* @param Controller $controller the controller that is being called
* @param string $methodName the name of the method that will be called on
Expand All @@ -187,4 +200,34 @@ public function afterException($controller, $methodName, \Exception $exception)

throw $exception;
}

/**
* Check if origin is allowed.
*
* @param string $origin The origin that will be checked
* @param string $app Optionally, the app that will provide the valid origin list
*
* @return bool
* True if origin is in allowed origins list.
*/
private function isOriginAllowed($origin, $app = null): bool {
// Starting with no allowed origins.
$allowed_origins = [];
// Add first the general allowed origins if defined
$cors_filter_settings_allowed_origins = $this->config->getAppValue('corsOriginFilterSettings', 'allowed_origins', []);
$cors_filter_settings_allowed_origins = explode(",", array_map('trim', $cors_filter_settings_allowed_origins));
$allowed_origins = [...$allowed_origins, ...$cors_filter_settings_allowed_origins];
$allowed_origins = [...$allowed_origins, ...$this->config->getSystemValue('allowed_origins', [])];

//Then add the app namespace specific allowed origins if defined
if ($app !== null) {
$cors_filter_settings_app_allowed_origins = $this->config->getAppValue('corsOriginFilterSettings', $app . 'allowed_origins', []);
$cors_filter_settings_app_allowed_origins = explode(",", array_map('trim', $cors_filter_settings_app_allowed_origins));
$allowed_origins = [...$allowed_origins, ...$cors_filter_settings_app_allowed_origins];
$allowed_origins = [...$allowed_origins, ...$this->config->getSystemValue($app . '.allowed_origins', [])];
}
$allowed_origins = array_map('trim', $allowed_origins);

return in_array($origin, $allowed_origins, true);
}
}