Add new CORS configuration options

This commit is contained in:
Alejandro Celaya
2025-07-05 10:34:50 +02:00
parent c8e3b3df0a
commit 92d7a44cee
7 changed files with 74 additions and 17 deletions

View File

@@ -86,6 +86,9 @@ enum EnvVars: string
case INITIAL_API_KEY = 'INITIAL_API_KEY';
case SKIP_INITIAL_GEOLITE_DOWNLOAD = 'SKIP_INITIAL_GEOLITE_DOWNLOAD';
case REAL_TIME_UPDATES_TOPICS = 'REAL_TIME_UPDATES_TOPICS';
case CORS_ALLOW_ORIGIN = 'CORS_ALLOW_ORIGIN';
case CORS_ALLOW_CREDENTIALS = 'CORS_ALLOW_CREDENTIALS';
case CORS_MAX_AGE = 'CORS_MAX_AGE';
/** @deprecated Use REDIRECT_EXTRA_PATH */
case REDIRECT_APPEND_EXTRA_PATH = 'REDIRECT_APPEND_EXTRA_PATH';
@@ -187,6 +190,10 @@ enum EnvVars: string
self::DISABLE_REFERRER_TRACKING,
self::DISABLE_UA_TRACKING => false,
self::CORS_ALLOW_ORIGIN => '*',
self::CORS_ALLOW_CREDENTIALS => false,
self::CORS_MAX_AGE => 3600,
default => null,
};
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Shlinkio\Shlink\Core\Config\Options;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Shlinkio\Shlink\Core\Config\EnvVars;
use function Shlinkio\Shlink\Core\ArrayUtils\contains;
use function Shlinkio\Shlink\Core\splitByComma;
final readonly class CorsOptions
{
private const string ORIGIN_PATTERN = '<origin>';
/** @var string[]|'*'|'<origin>' */
public string|array $allowOrigins;
public function __construct(
string $allowOrigins = '*',
public bool $allowCredentials = false,
public int $maxAge = 3600,
) {
$this->allowOrigins = $allowOrigins !== '*' && $allowOrigins !== self::ORIGIN_PATTERN
? splitByComma($allowOrigins)
: $allowOrigins;
}
public static function fromEnv(): self
{
return new self(
allowOrigins: EnvVars::CORS_ALLOW_ORIGIN->loadFromEnv(),
allowCredentials: EnvVars::CORS_ALLOW_CREDENTIALS->loadFromEnv(),
maxAge: EnvVars::CORS_MAX_AGE->loadFromEnv(),
);
}
public function responseWithAllowOrigin(RequestInterface $request, ResponseInterface $response): ResponseInterface
{
if ($this->allowOrigins === '*') {
return $response->withHeader('Access-Control-Allow-Origin', '*');
}
$requestOrigin = $request->getHeader('Origin');
if (
// The special <origin> value means we should allow requests from the origin set in the request's Origin
// header
$this->allowOrigins === self::ORIGIN_PATTERN
// If an array of allowed hosts was provided, set Access-Control-Allow-Origin header only if request's
// Origin header matches one of them
|| contains($requestOrigin, $this->allowOrigins)
) {
return $response->withHeader('Access-Control-Allow-Origin', $requestOrigin);
}
return $response;
}
}