mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a229cc1367 | ||
|
|
2dce141d17 | ||
|
|
d421b5ff30 | ||
|
|
b8eb0810c2 | ||
|
|
cb74a5756a | ||
|
|
ba25849871 | ||
|
|
44f3bbae03 |
@@ -3937,7 +3937,7 @@ Http::post('/v1/account/recovery')
|
||||
->setParam('userId', $profile->getId())
|
||||
->setParam('tokenId', $recovery->getId())
|
||||
->setUser($profile)
|
||||
->setPayload(Response::showSensitive(fn () => $response->output($recovery, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
->setPayload($response->showSensitive(fn () => $response->output($recovery, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -4038,7 +4038,7 @@ Http::put('/v1/account/recovery')
|
||||
$queueForEvents
|
||||
->setParam('userId', $profile->getId())
|
||||
->setParam('tokenId', $recoveryDocument->getId())
|
||||
->setPayload(Response::showSensitive(fn () => $response->output($recoveryDocument, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
->setPayload($response->showSensitive(fn () => $response->output($recoveryDocument, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
|
||||
$response->dynamic($recoveryDocument, Response::MODEL_TOKEN);
|
||||
});
|
||||
@@ -4268,7 +4268,7 @@ Http::post('/v1/account/verifications/email')
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId())
|
||||
->setParam('tokenId', $verification->getId())
|
||||
->setPayload(Response::showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
->setPayload($response->showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
@@ -4360,7 +4360,7 @@ Http::put('/v1/account/verifications/email')
|
||||
$queueForEvents
|
||||
->setParam('userId', $userId)
|
||||
->setParam('tokenId', $verification->getId())
|
||||
->setPayload(Response::showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
->setPayload($response->showSensitive(fn () => $response->output($verification, Response::MODEL_TOKEN)), sensitive: ['secret']);
|
||||
|
||||
$response->dynamic($verification, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
@@ -868,7 +868,7 @@ Http::init()
|
||||
* Request format
|
||||
*/
|
||||
$route = $utopia->getRoute();
|
||||
Request::setRoute($route);
|
||||
$request->setRoute($route);
|
||||
|
||||
if ($route === null) {
|
||||
$response->setStatusCode(404);
|
||||
@@ -1019,7 +1019,7 @@ Http::init()
|
||||
return;
|
||||
}
|
||||
$route = $request->getRoute();
|
||||
if ($route->getLabel('origin', false) === '*') {
|
||||
if ($route?->getLabel('origin', false) === '*') {
|
||||
return;
|
||||
}
|
||||
if (!$originValidator->isValid($origin)) {
|
||||
@@ -1493,6 +1493,19 @@ Http::error()
|
||||
'type' => $type,
|
||||
];
|
||||
|
||||
// Add CORS headers to error responses so browsers can read the error.
|
||||
// Wrapped in try-catch: if the error itself is a DB failure, resolving
|
||||
// the cors resource (which depends on rule -> DB) would cascade.
|
||||
// Uses override:true to avoid duplicate headers if init() already set them.
|
||||
try {
|
||||
$cors = $utopia->getResource('cors');
|
||||
foreach ($cors->headers($request->getOrigin()) as $name => $value) {
|
||||
$response->addHeader($name, $value, override: true);
|
||||
}
|
||||
} catch (Throwable) {
|
||||
// Degrade gracefully - error response without CORS is no worse than before.
|
||||
}
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
|
||||
->addHeader('Expires', '0')
|
||||
|
||||
@@ -383,6 +383,22 @@ $server->onStart(function () use ($stats, $containerId, &$statsDocument) {
|
||||
}
|
||||
});
|
||||
|
||||
function cloudRealtimeLogConnectionHostnames(Http $app, Document $project, Request $request): void
|
||||
{
|
||||
try {
|
||||
/** @var array<int, string> $allowed */
|
||||
$allowed = $app->getResource('allowedHostnames');
|
||||
Console::info(sprintf(
|
||||
'[Realtime] project=%s origin=%s allowedHostnames=%s',
|
||||
$project->getId(),
|
||||
$request->getOrigin(),
|
||||
json_encode(array_values($allowed))
|
||||
));
|
||||
} catch (Throwable $e) {
|
||||
Console::error('[Realtime] allowedHostnames log failed: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime) {
|
||||
Console::success('Worker ' . $workerId . ' started successfully');
|
||||
|
||||
@@ -688,6 +704,9 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
*/
|
||||
$origin = $request->getOrigin();
|
||||
$originValidator = $app->getResource('originValidator');
|
||||
|
||||
|
||||
cloudRealtimeLogConnectionHostnames($app, $project, $request);
|
||||
|
||||
if (!empty($origin) && !$originValidator->isValid($origin) && $project->getId() !== 'console') {
|
||||
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription());
|
||||
|
||||
@@ -17,7 +17,7 @@ class Request extends UtopiaRequest
|
||||
* @var array<Filter>
|
||||
*/
|
||||
private array $filters = [];
|
||||
private static ?Route $route = null;
|
||||
private ?Route $route = null;
|
||||
|
||||
public function __construct(SwooleRequest $request)
|
||||
{
|
||||
@@ -34,11 +34,11 @@ class Request extends UtopiaRequest
|
||||
{
|
||||
$parameters = parent::getParams();
|
||||
|
||||
if (!$this->hasFilters() || !self::hasRoute()) {
|
||||
if (!$this->hasFilters() || !$this->hasRoute()) {
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
$methods = self::getRoute()->getLabel('sdk', null);
|
||||
$methods = $this->getRoute()?->getLabel('sdk', null);
|
||||
|
||||
if (empty($methods)) {
|
||||
return $parameters;
|
||||
@@ -131,9 +131,9 @@ class Request extends UtopiaRequest
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setRoute(?Route $route): void
|
||||
public function setRoute(?Route $route): void
|
||||
{
|
||||
self::$route = $route;
|
||||
$this->route = $route;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,9 +141,9 @@ class Request extends UtopiaRequest
|
||||
*
|
||||
* @return Route|null
|
||||
*/
|
||||
public static function getRoute(): ?Route
|
||||
public function getRoute(): ?Route
|
||||
{
|
||||
return self::$route;
|
||||
return $this->route;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,9 +151,9 @@ class Request extends UtopiaRequest
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasRoute(): bool
|
||||
public function hasRoute(): bool
|
||||
{
|
||||
return self::$route !== null;
|
||||
return $this->route !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -299,7 +299,7 @@ class Response extends SwooleResponse
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected static bool $showSensitive = false;
|
||||
protected bool $showSensitive = false;
|
||||
|
||||
/**
|
||||
* @var array<string, Model>
|
||||
@@ -509,7 +509,7 @@ class Response extends SwooleResponse
|
||||
$isPrivilegedUser = $user->isPrivileged($roles);
|
||||
$isAppUser = $user->isApp($roles);
|
||||
|
||||
if ((!$isPrivilegedUser && !$isAppUser) && !self::$showSensitive) {
|
||||
if ((!$isPrivilegedUser && !$isAppUser) && !$this->showSensitive) {
|
||||
$data->setAttribute($key, '');
|
||||
}
|
||||
}
|
||||
@@ -659,18 +659,20 @@ class Response extends SwooleResponse
|
||||
}
|
||||
|
||||
/**
|
||||
* Static wrapper to show sensitive data in response
|
||||
* Wrapper to show sensitive data in response
|
||||
*
|
||||
* @param callable(): array $callback The callback to show sensitive information for
|
||||
* @return array
|
||||
*/
|
||||
public static function showSensitive(callable $callback): array
|
||||
public function showSensitive(callable $callback): array
|
||||
{
|
||||
$previous = $this->showSensitive;
|
||||
|
||||
try {
|
||||
self::$showSensitive = true;
|
||||
$this->showSensitive = true;
|
||||
return $callback();
|
||||
} finally {
|
||||
self::$showSensitive = false;
|
||||
$this->showSensitive = $previous;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -147,6 +147,21 @@ class RequestTest extends TestCase
|
||||
$this->assertSame('unexpected', $params['extra']);
|
||||
}
|
||||
|
||||
public function testRouteIsScopedToRequestInstance(): void
|
||||
{
|
||||
$firstRequest = new Request(new SwooleRequest());
|
||||
$secondRequest = new Request(new SwooleRequest());
|
||||
|
||||
$firstRoute = new Route(Request::METHOD_GET, '/first');
|
||||
$secondRoute = new Route(Request::METHOD_GET, '/second');
|
||||
|
||||
$firstRequest->setRoute($firstRoute);
|
||||
$secondRequest->setRoute($secondRoute);
|
||||
|
||||
$this->assertSame($firstRoute, $firstRequest->getRoute());
|
||||
$this->assertSame($secondRoute, $secondRequest->getRoute());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to attach a route with multiple SDK methods to the request.
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Tests\Unit\Utopia;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Exception;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use ReflectionProperty;
|
||||
use Swoole\Http\Response as SwooleResponse;
|
||||
use Tests\Unit\Utopia\Response\Filters\First;
|
||||
use Tests\Unit\Utopia\Response\Filters\Second;
|
||||
@@ -176,4 +177,26 @@ class ResponseTest extends TestCase
|
||||
$this->assertArrayHasKey('required', $single);
|
||||
$this->assertArrayNotHasKey('hidden', $singleFromArray);
|
||||
}
|
||||
|
||||
public function testShowSensitiveRestoresPreviousState(): void
|
||||
{
|
||||
$isShowingSensitive = new ReflectionProperty(Response::class, 'showSensitive');
|
||||
|
||||
$this->assertFalse($isShowingSensitive->getValue($this->response));
|
||||
|
||||
$payload = $this->response->showSensitive(function () use ($isShowingSensitive) {
|
||||
return [
|
||||
'outer' => $isShowingSensitive->getValue($this->response),
|
||||
'inner' => $this->response->showSensitive(fn () => [
|
||||
'state' => $isShowingSensitive->getValue($this->response),
|
||||
]),
|
||||
'afterInner' => $isShowingSensitive->getValue($this->response),
|
||||
];
|
||||
});
|
||||
|
||||
$this->assertTrue($payload['outer']);
|
||||
$this->assertTrue($payload['inner']['state']);
|
||||
$this->assertTrue($payload['afterInner']);
|
||||
$this->assertFalse($isShowingSensitive->getValue($this->response));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user