mirror of
https://github.com/appwrite/appwrite
synced 2026-05-24 09:28:40 +00:00
Merge branch '1.8.x' into fix/tableDbBrokenLink
This commit is contained in:
commit
9d4592d90e
3 changed files with 190 additions and 27 deletions
|
|
@ -151,7 +151,7 @@ App::setResource('queueForMigrations', function (Publisher $publisher) {
|
||||||
App::setResource('queueForStatsResources', function (Publisher $publisher) {
|
App::setResource('queueForStatsResources', function (Publisher $publisher) {
|
||||||
return new StatsResources($publisher);
|
return new StatsResources($publisher);
|
||||||
}, ['publisher']);
|
}, ['publisher']);
|
||||||
App::setResource('platforms', function (Request $request, Document $console, Document $project) {
|
App::setResource('platforms', function (Request $request, Document $console, Document $project, Database $dbForPlatform) {
|
||||||
$console->setAttribute('platforms', [ // Always allow current host
|
$console->setAttribute('platforms', [ // Always allow current host
|
||||||
'$collection' => ID::custom('platforms'),
|
'$collection' => ID::custom('platforms'),
|
||||||
'name' => 'Current Host',
|
'name' => 'Current Host',
|
||||||
|
|
@ -190,11 +190,40 @@ App::setResource('platforms', function (Request $request, Document $console, Doc
|
||||||
], Document::SET_TYPE_APPEND);
|
], Document::SET_TYPE_APPEND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$origin = \parse_url($request->getOrigin(), PHP_URL_HOST);
|
||||||
|
|
||||||
|
if (empty($origin)) {
|
||||||
|
$origin = \parse_url($request->getReferer(), PHP_URL_HOST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe if rule with same project ID exists
|
||||||
|
if (!empty($origin)) {
|
||||||
|
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
|
||||||
|
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
|
||||||
|
} else {
|
||||||
|
$rule = Authorization::skip(
|
||||||
|
fn () => $dbForPlatform->find('rules', [
|
||||||
|
Query::equal('domain', [$origin]),
|
||||||
|
Query::limit(1)
|
||||||
|
])
|
||||||
|
)[0] ?? new Document();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) {
|
||||||
|
$project->setAttribute('platforms', [
|
||||||
|
'$collection' => ID::custom('platforms'),
|
||||||
|
'type' => Platform::TYPE_WEB,
|
||||||
|
'name' => $origin,
|
||||||
|
'hostname' => $origin,
|
||||||
|
], Document::SET_TYPE_APPEND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...$console->getAttribute('platforms', []),
|
...$console->getAttribute('platforms', []),
|
||||||
...$project->getAttribute('platforms', []),
|
...$project->getAttribute('platforms', []),
|
||||||
];
|
];
|
||||||
}, ['request', 'console', 'project']);
|
}, ['request', 'console', 'project', 'dbForPlatform']);
|
||||||
|
|
||||||
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) {
|
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) {
|
||||||
/** @var Appwrite\Utopia\Request $request */
|
/** @var Appwrite\Utopia\Request $request */
|
||||||
|
|
@ -375,7 +404,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
|
||||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||||
$database
|
$database
|
||||||
->setSharedTables(true)
|
->setSharedTables(true)
|
||||||
->setTenant((int)$project->getSequence())
|
->setTenant((int) $project->getSequence())
|
||||||
->setNamespace($dsn->getParam('namespace'));
|
->setNamespace($dsn->getParam('namespace'));
|
||||||
} else {
|
} else {
|
||||||
$database
|
$database
|
||||||
|
|
@ -428,7 +457,7 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
|
||||||
if (\in_array($dsn->getHost(), $sharedTables)) {
|
if (\in_array($dsn->getHost(), $sharedTables)) {
|
||||||
$database
|
$database
|
||||||
->setSharedTables(true)
|
->setSharedTables(true)
|
||||||
->setTenant((int)$project->getSequence())
|
->setTenant((int) $project->getSequence())
|
||||||
->setNamespace($dsn->getParam('namespace'));
|
->setNamespace($dsn->getParam('namespace'));
|
||||||
} else {
|
} else {
|
||||||
$database
|
$database
|
||||||
|
|
@ -458,7 +487,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||||
|
|
||||||
return function (?Document $project = null) use ($pools, $cache, &$database) {
|
return function (?Document $project = null) use ($pools, $cache, &$database) {
|
||||||
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||||
$database->setTenant((int)$project->getSequence());
|
$database->setTenant((int) $project->getSequence());
|
||||||
return $database;
|
return $database;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -473,7 +502,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
|
||||||
|
|
||||||
// set tenant
|
// set tenant
|
||||||
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
|
||||||
$database->setTenant((int)$project->getSequence());
|
$database->setTenant((int) $project->getSequence());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $database;
|
return $database;
|
||||||
|
|
@ -501,7 +530,7 @@ App::setResource('redis', function () {
|
||||||
$pass = System::getEnv('_APP_REDIS_PASS', '');
|
$pass = System::getEnv('_APP_REDIS_PASS', '');
|
||||||
|
|
||||||
$redis = new \Redis();
|
$redis = new \Redis();
|
||||||
@$redis->pconnect($host, (int)$port);
|
@$redis->pconnect($host, (int) $port);
|
||||||
if ($pass) {
|
if ($pass) {
|
||||||
$redis->auth($pass);
|
$redis->auth($pass);
|
||||||
}
|
}
|
||||||
|
|
@ -714,7 +743,7 @@ App::setResource('schema', function ($utopia, $dbForProject) {
|
||||||
// NOTE: `params` and `urls` are not used internally in the `Schema::build` function below!
|
// NOTE: `params` and `urls` are not used internally in the `Schema::build` function below!
|
||||||
$params = [
|
$params = [
|
||||||
'list' => function (string $databaseId, string $collectionId, array $args) {
|
'list' => function (string $databaseId, string $collectionId, array $args) {
|
||||||
return [ 'queries' => $args['queries']];
|
return ['queries' => $args['queries']];
|
||||||
},
|
},
|
||||||
'create' => function (string $databaseId, string $collectionId, array $args) {
|
'create' => function (string $databaseId, string $collectionId, array $args) {
|
||||||
$id = $args['id'] ?? 'unique()';
|
$id = $args['id'] ?? 'unique()';
|
||||||
|
|
@ -963,7 +992,7 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$accessedAt = $token->getAttribute('accessedAt', 0);
|
$accessedAt = $token->getAttribute('accessedAt', 0);
|
||||||
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), - APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) {
|
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) {
|
||||||
$token->setAttribute('accessedAt', DatabaseDateTime::now());
|
$token->setAttribute('accessedAt', DatabaseDateTime::now());
|
||||||
Authorization::skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token));
|
Authorization::skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token));
|
||||||
}
|
}
|
||||||
|
|
@ -1005,24 +1034,6 @@ App::setResource('httpReferrerSafe', function (Request $request, string $httpRef
|
||||||
return $referrer;
|
return $referrer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe if rule with same project ID exists
|
|
||||||
if (!empty($origin)) {
|
|
||||||
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
|
|
||||||
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin ?? '')));
|
|
||||||
} else {
|
|
||||||
$rule = Authorization::skip(
|
|
||||||
fn () => $dbForPlatform->find('rules', [
|
|
||||||
Query::equal('domain', [$origin]),
|
|
||||||
Query::limit(1)
|
|
||||||
])
|
|
||||||
)[0] ?? new Document();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getSequence()) {
|
|
||||||
return $referrer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsafe; Localhost is always safe for ease of local development
|
// Unsafe; Localhost is always safe for ease of local development
|
||||||
$origin = 'localhost';
|
$origin = 'localhost';
|
||||||
$protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME);
|
$protocol = \parse_url($request->getOrigin($httpReferrer), PHP_URL_SCHEME);
|
||||||
|
|
|
||||||
|
|
@ -592,6 +592,7 @@ class Event
|
||||||
$this->project = $event->getProject();
|
$this->project = $event->getProject();
|
||||||
$this->user = $event->getUser();
|
$this->user = $event->getUser();
|
||||||
$this->payload = $event->getPayload();
|
$this->payload = $event->getPayload();
|
||||||
|
$this->sensitive = $event->sensitive;
|
||||||
$this->event = $event->getEvent();
|
$this->event = $event->getEvent();
|
||||||
$this->params = $event->getParams();
|
$this->params = $event->getParams();
|
||||||
$this->context = $event->context;
|
$this->context = $event->context;
|
||||||
|
|
|
||||||
|
|
@ -2507,4 +2507,155 @@ class RealtimeCustomClientTest extends Scope
|
||||||
|
|
||||||
$client->close();
|
$client->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testRelationshipPayloadHidesRelatedDoc()
|
||||||
|
{
|
||||||
|
$user = $this->getUser();
|
||||||
|
$session = $user['session'] ?? '';
|
||||||
|
$projectId = $this->getProject()['$id'];
|
||||||
|
|
||||||
|
$client = $this->getWebsocket(['documents'], [
|
||||||
|
'origin' => 'http://localhost',
|
||||||
|
'cookie' => 'a_session_' . $projectId . '=' . $session
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = json_decode($client->receive(), true);
|
||||||
|
$this->assertArrayHasKey('type', $response);
|
||||||
|
$this->assertEquals('connected', $response['type']);
|
||||||
|
|
||||||
|
// Create database
|
||||||
|
$database = $this->client->call(Client::METHOD_POST, '/databases', array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'databaseId' => ID::unique(),
|
||||||
|
'name' => 'db-rel'
|
||||||
|
]);
|
||||||
|
$databaseId = $database['body']['$id'];
|
||||||
|
|
||||||
|
$level1 = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'collectionId' => ID::unique(),
|
||||||
|
'name' => 'level1',
|
||||||
|
'permissions' => [
|
||||||
|
Permission::read(Role::any()),
|
||||||
|
Permission::create(Role::any()),
|
||||||
|
Permission::update(Role::any()),
|
||||||
|
Permission::delete(Role::any()),
|
||||||
|
],
|
||||||
|
'documentSecurity' => true,
|
||||||
|
]);
|
||||||
|
$level1Id = $level1['body']['$id'];
|
||||||
|
|
||||||
|
$level2 = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections", array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'collectionId' => ID::unique(),
|
||||||
|
'name' => 'level2',
|
||||||
|
'permissions' => [
|
||||||
|
Permission::read(Role::any()),
|
||||||
|
Permission::create(Role::any()),
|
||||||
|
Permission::update(Role::any()),
|
||||||
|
Permission::delete(Role::any()),
|
||||||
|
],
|
||||||
|
'documentSecurity' => true,
|
||||||
|
]);
|
||||||
|
$level2Id = $level2['body']['$id'];
|
||||||
|
|
||||||
|
$this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$level1Id}/attributes/string", array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'key' => 'name',
|
||||||
|
'size' => 256,
|
||||||
|
'required' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$level2Id}/attributes/string", array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'key' => 'name',
|
||||||
|
'size' => 256,
|
||||||
|
'required' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
sleep(2);
|
||||||
|
|
||||||
|
// two-way one-to-one relationship from level1 to level2
|
||||||
|
$this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$level1Id}/attributes/relationship", array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'relatedCollectionId' => $level2Id,
|
||||||
|
'type' => 'oneToOne',
|
||||||
|
'twoWay' => true,
|
||||||
|
'key' => 'level2Ref',
|
||||||
|
'onDelete' => 'cascade',
|
||||||
|
]);
|
||||||
|
|
||||||
|
sleep(2);
|
||||||
|
|
||||||
|
$doc2 = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$level2Id}/documents", array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'documentId' => ID::unique(),
|
||||||
|
'data' => [ 'name' => 'L2' ],
|
||||||
|
'permissions' => [
|
||||||
|
Permission::read(Role::any()),
|
||||||
|
Permission::update(Role::any()),
|
||||||
|
Permission::delete(Role::any()),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$doc2Id = $doc2['body']['$id'];
|
||||||
|
|
||||||
|
$doc1 = $this->client->call(Client::METHOD_POST, "/databases/{$databaseId}/collections/{$level1Id}/documents", array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
], $this->getHeaders()), [
|
||||||
|
'documentId' => ID::unique(),
|
||||||
|
'data' => [ 'name' => 'L1' ],
|
||||||
|
'permissions' => [
|
||||||
|
Permission::read(Role::any()),
|
||||||
|
Permission::update(Role::any()),
|
||||||
|
Permission::delete(Role::any()),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$doc1Id = $doc1['body']['$id'];
|
||||||
|
|
||||||
|
json_decode($client->receive(), true);
|
||||||
|
|
||||||
|
$this->client->call(Client::METHOD_PATCH, "/databases/{$databaseId}/collections/{$level1Id}/documents/{$doc1Id}", array_merge([
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'x-appwrite-project' => $projectId,
|
||||||
|
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||||
|
]), [
|
||||||
|
'data' => [
|
||||||
|
'level2Ref' => $doc2Id,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// payload should not contain the relationship attribute 'level2Ref'
|
||||||
|
$event = json_decode($client->receive(), true);
|
||||||
|
$this->assertArrayHasKey('type', $event);
|
||||||
|
$this->assertEquals('event', $event['type']);
|
||||||
|
$this->assertArrayHasKey('data', $event);
|
||||||
|
$this->assertNotEmpty($event['data']);
|
||||||
|
$this->assertArrayHasKey('payload', $event['data']);
|
||||||
|
$this->assertArrayHasKey('$id', $event['data']['payload']);
|
||||||
|
$this->assertEquals($doc1Id, $event['data']['payload']['$id']);
|
||||||
|
$this->assertArrayNotHasKey('level2Ref', $event['data']['payload']);
|
||||||
|
|
||||||
|
$client->close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue