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);