Skip to content

Commit 2e0165b

Browse files
authored
Merge pull request #536 from utopia-php/fix-upsert-permissions
Make sure auth is skipped when appropriate for upsert
2 parents 9ac7e09 + c9630ff commit 2e0165b

File tree

3 files changed

+78
-50
lines changed

3 files changed

+78
-50
lines changed

composer.lock

Lines changed: 24 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Database/Adapter/SQL.php

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,24 +1050,29 @@ protected function getSQLIndexType(string $type): string
10501050
*
10511051
* @param string $collection
10521052
* @param array<string> $roles
1053+
* @param string $type
10531054
* @return string
1054-
* @throws Exception
1055+
* @throws DatabaseException
10551056
*/
1056-
protected function getSQLPermissionsCondition(string $collection, array $roles, string $type = Database::PERMISSION_READ): string
1057-
{
1058-
if (!in_array($type, Database::PERMISSIONS)) {
1057+
protected function getSQLPermissionsCondition(
1058+
string $collection,
1059+
array $roles,
1060+
string $type = Database::PERMISSION_READ
1061+
): string {
1062+
if (!\in_array($type, Database::PERMISSIONS)) {
10591063
throw new DatabaseException('Unknown permission type: ' . $type);
10601064
}
10611065

1062-
$roles = array_map(fn (string $role) => $this->getPDO()->quote($role), $roles);
1066+
$roles = \array_map(fn ($role) => $this->getPDO()->quote($role), $roles);
1067+
$roles = \implode(', ', $roles);
10631068

10641069
return "table_main._uid IN (
1065-
SELECT _document
1066-
FROM {$this->getSQLTable($collection . '_perms')}
1067-
WHERE _permission IN (" . implode(', ', $roles) . ")
1068-
AND _type = '{$type}'
1069-
{$this->getTenantQuery($collection)}
1070-
)";
1070+
SELECT _document
1071+
FROM {$this->getSQLTable($collection . '_perms')}
1072+
WHERE _permission IN ({$roles})
1073+
AND _type = '{$type}'
1074+
{$this->getTenantQuery($collection)}
1075+
)";
10711076
}
10721077

10731078
/**

src/Database/Database.php

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3477,7 +3477,7 @@ public function createDocuments(
34773477
$stack = [];
34783478

34793479
foreach (\array_chunk($documents, $batchSize) as $chunk) {
3480-
$stack = array_merge($stack, $this->adapter->createDocuments($collection->getId(), $chunk));
3480+
\array_push($stack, ...$this->adapter->createDocuments($collection->getId(), $chunk));
34813481
}
34823482

34833483
return $stack;
@@ -4592,6 +4592,7 @@ private function getJunctionCollection(Document $collection, Document $relatedCo
45924592
* @param int $batchSize
45934593
* @return array<Document>
45944594
* @throws StructureException
4595+
* @throws \Throwable
45954596
*/
45964597
public function createOrUpdateDocuments(
45974598
string $collection,
@@ -4629,23 +4630,37 @@ public function createOrUpdateDocumentsWithIncrease(
46294630
}
46304631

46314632
$batchSize = \min(Database::INSERT_BATCH_SIZE, \max(1, $batchSize));
4632-
46334633
$collection = $this->silent(fn () => $this->getCollection($collection));
4634-
4634+
$documentSecurity = $collection->getAttribute('documentSecurity', false);
46354635
$time = DateTime::now();
46364636

46374637
foreach ($documents as $key => $document) {
4638-
$old = Authorization::skip(fn () => $this->silent(fn () => $this->getDocument($collection->getId(), $document->getId())));
4639-
4640-
if (!$old->isEmpty()) {
4641-
$validator = new Authorization(self::PERMISSION_UPDATE);
4638+
$old = Authorization::skip(fn () => $this->silent(fn () => $this->getDocument(
4639+
$collection->getId(),
4640+
$document->getId(),
4641+
[Query::select(['$internalId', '$permissions'])],
4642+
forUpdate: true
4643+
)));
4644+
4645+
// If old is empty, check if user has create permission on the collection
4646+
// If old is not empty, check if user has update permission on the collection
4647+
// If old is not empty AND documentSecurity is enabled, we need to check if user has update permission on the collection or document
4648+
4649+
$validator = new Authorization(
4650+
$old->isEmpty() ?
4651+
self::PERMISSION_CREATE :
4652+
self::PERMISSION_UPDATE
4653+
);
46424654

4643-
if (!$validator->isValid([
4644-
...$collection->getUpdate(),
4645-
...($collection->getAttribute('documentSecurity') ? $old->getUpdate() : [])
4646-
])) {
4655+
if ($old->isEmpty()) {
4656+
if (!$validator->isValid($collection->getCreate())) {
46474657
throw new AuthorizationException($validator->getDescription());
46484658
}
4659+
} elseif (!$validator->isValid([
4660+
...$collection->getUpdate(),
4661+
...($documentSecurity ? $old->getUpdate() : [])
4662+
])) {
4663+
throw new AuthorizationException($validator->getDescription());
46494664
}
46504665

46514666
$createdAt = $document->getCreatedAt();
@@ -4680,7 +4695,14 @@ public function createOrUpdateDocumentsWithIncrease(
46804695
$stack = [];
46814696

46824697
foreach (\array_chunk($documents, $batchSize) as $chunk) {
4683-
$stack = array_merge($stack, $this->adapter->createOrUpdateDocuments($collection->getId(), $attribute, $chunk));
4698+
\array_push(
4699+
$stack,
4700+
...Authorization::skip(fn () => $this->adapter->createOrUpdateDocuments(
4701+
$collection->getId(),
4702+
$attribute,
4703+
$chunk
4704+
))
4705+
);
46844706
}
46854707

46864708
return $stack;
@@ -5347,6 +5369,7 @@ private function deleteCascade(Document $collection, Document $relatedCollection
53475369
* @throws AuthorizationException
53485370
* @throws DatabaseException
53495371
* @throws RestrictedException
5372+
* @throws \Throwable
53505373
*/
53515374
public function deleteDocuments(string $collection, array $queries = [], int $batchSize = self::DELETE_BATCH_SIZE): array
53525375
{
@@ -5461,12 +5484,12 @@ public function deleteDocuments(string $collection, array $queries = [], int $ba
54615484
}
54625485

54635486
foreach (\array_chunk($documents, $batchSize) as $chunk) {
5464-
$callback = fn () => $this->adapter->deleteDocuments(
5487+
$getResults = fn () => $this->adapter->deleteDocuments(
54655488
$collection->getId(),
54665489
array_map(fn ($document) => $document->getId(), $chunk)
54675490
);
54685491

5469-
$skipAuth ? $authorization->skip($callback) : $callback();
5492+
$skipAuth ? $authorization->skip($getResults) : $getResults();
54705493
}
54715494

54725495
return $documents;
@@ -5568,7 +5591,7 @@ public function find(string $collection, array $queries = [], string $forPermiss
55685591
}
55695592
}
55705593

5571-
$authorization = new Authorization(self::PERMISSION_READ);
5594+
$authorization = new Authorization($forPermission);
55725595
$documentSecurity = $collection->getAttribute('documentSecurity', false);
55735596
$skipAuth = $authorization->isValid($collection->getPermissionsByType($forPermission));
55745597

0 commit comments

Comments
 (0)