From eb8e7b3415c77c9274e4465f68d19857be940c06 Mon Sep 17 00:00:00 2001 From: jo Date: Mon, 23 May 2022 17:12:40 +0200 Subject: [PATCH] feat: move allowed cors url to configuration file - don't set cors origins form field as readonly and add deprecation notice. --- docs/releases/unreleased.md | 4 + installer/config.yml | 4 + legacy/application/common/CORSHelper.php | 73 +++++++++++++------ legacy/application/configs/conf.php | 4 + .../application/forms/GeneralPreferences.php | 4 +- 5 files changed, 63 insertions(+), 26 deletions(-) diff --git a/docs/releases/unreleased.md b/docs/releases/unreleased.md index e4c31d0d4..b4c70707a 100644 --- a/docs/releases/unreleased.md +++ b/docs/releases/unreleased.md @@ -16,6 +16,10 @@ The LibreTime project wants to thank the following contributors for authoring PR ## :fire: Deprecation and removal +### Allowed CORS origins configuration location + +The allowed CORS origins configuration moved from the database to the configuration file. The field in the general preference form is deprecated and will be removed in the next release. Be sure to move your allowed CORS origins configuration to the [configuration file](../admin-manual/setup/configuration.md). + ## :arrow_up: Upgrading ### New configuration file diff --git a/installer/config.yml b/installer/config.yml index a38b969c6..6d577ad8b 100644 --- a/installer/config.yml +++ b/installer/config.yml @@ -6,6 +6,10 @@ general: # The internal API authentication key, this field is REQUIRED api_key: + # List of origins allowed to access resources on the server, default is [] + # The public url origin is automatically included + allowed_cors_origins: [] + # How many hours ahead Playout should cache scheduled media files, default is 1 cache_ahead_hours: 1 diff --git a/legacy/application/common/CORSHelper.php b/legacy/application/common/CORSHelper.php index bd8f0d1c8..24fd88408 100644 --- a/legacy/application/common/CORSHelper.php +++ b/legacy/application/common/CORSHelper.php @@ -1,5 +1,8 @@ getHeader('Origin'); $allowedOrigins = self::getAllowedOrigins($request); - if ((!(preg_match('/https?:\/\/localhost/', $origin) === 1)) && ($origin != '') - && (!in_array($origin, $allowedOrigins)) - ) { + if (!($origin == '' || preg_match('/https?:\/\/localhost/', $origin) === 1 || in_array($origin, $allowedOrigins))) { // Don't allow CORS from other domains to prevent XSS. Logging::error("request origin '{$origin}' is not in allowed '" . implode(', ', $allowedOrigins) . "'!"); @@ -29,32 +30,56 @@ class CORSHelper */ public static function getAllowedOrigins($request) { - $allowedCorsUrls = array_map( - function ($v) { return trim($v); }, - explode(PHP_EOL, Application_Model_Preference::GetAllowedCorsUrls()) - ); + $config = Config::getConfig(); - // always allow the configured server in (as reported by the server and not what is i baseUrl) + return array_merge( + $config['allowedCorsOrigins'], + self::getDatabaseAllowedOrigins(), + self::getServerAllowedOrigins($request), + ); + } + + /** + * Get configured server origins. + * + * @param Request $request request object + * + * @return array + */ + private static function getServerAllowedOrigins($request) + { $scheme = $request->getServer('REQUEST_SCHEME'); $host = $request->getServer('SERVER_NAME'); - $port = $request->getServer('SERVER_PORT'); + $port = intval($request->getServer('SERVER_PORT')); - $portString = ''; - if ( - $scheme == 'https' && $port != 443 - || $scheme == 'http' && $port != 80 - ) { - $portString = sprintf(':%s', $port); + try { + return [ + strval(Uri::createFromComponents([ + 'scheme' => $scheme, + 'host' => $host, + 'port' => $port, + ])), + ]; + } catch (UriException|TypeError $e) { + Logging::warn("could not parse server origin : {$e}"); + + return []; } - $requestedUrl = sprintf( - '%s://%s%s', - $scheme, - $host, - $portString - ); + } - return array_merge($allowedCorsUrls, [ - $requestedUrl, - ]); + /** + * Get database allowed origins. + * + * @return array + */ + private static function getDatabaseAllowedOrigins() + { + return array_map( + 'trim', + explode( + PHP_EOL, + Application_Model_Preference::GetAllowedCorsUrls(), + ) + ); } } diff --git a/legacy/application/configs/conf.php b/legacy/application/configs/conf.php index f1cd1638d..f94ae52ad 100644 --- a/legacy/application/configs/conf.php +++ b/legacy/application/configs/conf.php @@ -44,6 +44,10 @@ class Config $CC_CONFIG['basePort'] = $port; $CC_CONFIG['baseDir'] = $path; + // Allowed hosts + $CC_CONFIG['allowedCorsOrigins'] = $values['general']['allowed_cors_origins'] ?? []; + $CC_CONFIG['allowedCorsOrigins'][] = strval($public_url->withPath('')); + $CC_CONFIG['dev_env'] = $values['general']['dev_env'] ?? 'production'; $CC_CONFIG['auth'] = $values['general']['auth'] ?? 'local'; $CC_CONFIG['cache_ahead_hours'] = $values['general']['cache_ahead_hours'] ?? 1; diff --git a/legacy/application/forms/GeneralPreferences.php b/legacy/application/forms/GeneralPreferences.php index 25af4dddc..1ca5dceab 100644 --- a/legacy/application/forms/GeneralPreferences.php +++ b/legacy/application/forms/GeneralPreferences.php @@ -170,8 +170,8 @@ class Application_Form_GeneralPreferences extends Zend_Form_SubForm $allowedCorsUrlsValue = Application_Model_Preference::GetAllowedCorsUrls(); $allowedCorsUrls = new Zend_Form_Element_Textarea('allowedCorsUrls'); - $allowedCorsUrls->setLabel(_('Allowed CORS URLs')); - $allowedCorsUrls->setDescription(_('Remote URLs that are allowed to access this LibreTime instance in a browser. One URL per line.')); + $allowedCorsUrls->setLabel(_('Allowed CORS URLs (DEPRECATED)')); + $allowedCorsUrls->setDescription(_('Remote URLs that are allowed to access this LibreTime instance in a browser. One URL per line. (DEPRECATED: Allowed CORS origins configuration moved to the configuration file.)')); $allowedCorsUrls->setValue($allowedCorsUrlsValue); $this->addElement($allowedCorsUrls);