Skip to content

Commit f4ccbc2

Browse files
Merge pull request #301 from utopia-php/fix-5404-permission-check-update-document
2 parents 5ae3035 + 0d9a648 commit f4ccbc2

File tree

2 files changed

+74
-3
lines changed

2 files changed

+74
-3
lines changed

src/Database/Database.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2881,20 +2881,30 @@ public function updateDocument(string $collection, string $id, Document $documen
28812881
$old = Authorization::skip(fn () => $this->silent(fn () => $this->getDocument($collection, $id))); // Skip ensures user does not need read permission for this
28822882

28832883
$collection = $this->silent(fn () => $this->getCollection($collection));
2884+
$relationships = \array_filter($collection->getAttribute('attributes', []), function ($attribute) {
2885+
return $attribute['type'] === Database::VAR_RELATIONSHIP;
2886+
});
28842887

28852888
$validator = new Authorization(self::PERMISSION_UPDATE);
28862889
$shouldUpdate = false;
28872890

28882891
if ($collection->getId() !== self::METADATA) {
28892892
$documentSecurity = $collection->getAttribute('documentSecurity', false);
28902893

2894+
$relationshipKeys = [];
2895+
foreach ($relationships as $relationship) {
2896+
$relationshipKey = $relationship->getAttribute('key');
2897+
$relationshipKeys[$relationshipKey] = $relationshipKey;
2898+
}
28912899
// Compare if the document has any changes
28922900
foreach ($document as $key=>$value) {
28932901
// Skip the nested documents as they will be checked later in recursions.
2894-
$oldAttributeValue = $old->getAttribute($key);
2895-
if ($oldAttributeValue instanceof Document) {
2902+
if (array_key_exists($key, $relationshipKeys)) {
28962903
continue;
28972904
}
2905+
2906+
$oldAttributeValue = $old->getAttribute($key);
2907+
// If values are not equal we need to update document.
28982908
if ($oldAttributeValue !== $value) {
28992909
$shouldUpdate = true;
29002910
break;
@@ -2910,6 +2920,8 @@ public function updateDocument(string $collection, string $id, Document $documen
29102920

29112921
if ($shouldUpdate) {
29122922
$document->setAttribute('$updatedAt', $time);
2923+
} else {
2924+
$document->setAttribute('$updatedAt', $old->getUpdatedAt());
29132925
}
29142926

29152927
// Check if document was updated after the request timestamp

tests/Database/Base.php

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3064,6 +3064,65 @@ public function testNoChangeUpdateDocumentWithoutPermission(Document $document):
30643064
return $document;
30653065
}
30663066

3067+
public function testNoChangeUpdateDocumentWithRelationWithoutPermission(): void
3068+
{
3069+
if (!static::getDatabase()->getAdapter()->getSupportForRelationships()) {
3070+
$this->expectNotToPerformAssertions();
3071+
return;
3072+
}
3073+
3074+
Authorization::cleanRoles();
3075+
Authorization::setRole(Role::user('a')->toString());
3076+
3077+
static::getDatabase()->createCollection('parentRelationTest', [], [], [
3078+
Permission::read(Role::user('a')),
3079+
Permission::create(Role::user('a')),
3080+
]);
3081+
static::getDatabase()->createCollection('childRelationTest', [], [], [
3082+
Permission::read(Role::user('a')),
3083+
Permission::create(Role::user('a')),
3084+
]);
3085+
static::getDatabase()->createAttribute('parentRelationTest', 'name', Database::VAR_STRING, 255, false);
3086+
static::getDatabase()->createAttribute('childRelationTest', 'name', Database::VAR_STRING, 255, false);
3087+
3088+
static::getDatabase()->createRelationship(
3089+
collection: 'parentRelationTest',
3090+
relatedCollection: 'childRelationTest',
3091+
type: Database::RELATION_ONE_TO_MANY,
3092+
id: 'childs'
3093+
);
3094+
3095+
// Create document with relationship with nested data
3096+
$parent = static::getDatabase()->createDocument('parentRelationTest', new Document([
3097+
'$id' => 'parent1',
3098+
'name' => 'Parent 1',
3099+
'childs' => [
3100+
[
3101+
'$id' => 'child1',
3102+
'name' => 'Child 1',
3103+
],
3104+
],
3105+
]));
3106+
$this->assertEquals(1, \count($parent['childs']));
3107+
$updatedParent = static::getDatabase()->updateDocument('parentRelationTest', 'parent1', new Document([
3108+
'$id' => 'parent1',
3109+
'name'=>'Parent 1',
3110+
'$collection' => 'parentRelationTest',
3111+
'childs' => [
3112+
new Document([
3113+
'$id' => 'child1',
3114+
'$collection' => 'childRelationTest'
3115+
]),
3116+
]
3117+
]));
3118+
3119+
$this->assertEquals($updatedParent->getUpdatedAt(), $parent->getUpdatedAt());
3120+
$this->assertEquals($updatedParent->getAttribute('childs')[0]->getUpdatedAt(), $parent->getAttribute('childs')[0]->getUpdatedAt());
3121+
3122+
static::getDatabase()->deleteCollection('parentRelationTest');
3123+
static::getDatabase()->deleteCollection('childRelationTest');
3124+
}
3125+
30673126
public function testExceptionAttributeLimit(): void
30683127
{
30693128
if ($this->getDatabase()->getLimitForAttributes() > 0) {
@@ -11216,7 +11275,7 @@ public function testCollectionPermissionsRelationshipsUpdateThrowsException(arra
1121611275
$document = static::getDatabase()->updateDocument(
1121711276
$collection->getId(),
1121811277
$document->getId(),
11219-
$document->setAttribute('test', 'ipsum')
11278+
$document->setAttribute('test', $document->getAttribute('test').'new_value')
1122011279
);
1122111280
}
1122211281

0 commit comments

Comments
 (0)