Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crud operations permission in update relationships #300

Merged
merged 11 commits into from
Aug 21, 2023
71 changes: 46 additions & 25 deletions src/Database/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -2752,7 +2752,7 @@ private function relateDocuments(
}

// Try to get the related document
$related = $this->getDocument($relatedCollection->getId(), $relation->getId());
$related = Authorization::skip(fn () => $this->getDocument($relatedCollection->getId(), $relation->getId()));

if ($related->isEmpty()) {
// If the related document doesn't exist, create it, inheriting permissions if none are set
Expand All @@ -2773,15 +2773,15 @@ private function relateDocuments(
if ($relationType === Database::RELATION_MANY_TO_MANY) {
$junction = $this->getJunctionCollection($collection, $relatedCollection, $side);

$this->createDocument($junction, new Document([
Authorization::skip(fn () => $this->createDocument($junction, new Document([
$key => $related->getId(),
$twoWayKey => $document->getId(),
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
]
]));
])));
}

return $related->getId();
Expand Down Expand Up @@ -2885,6 +2885,10 @@ public function updateDocument(string $collection, string $id, Document $documen
return $attribute['type'] === Database::VAR_RELATIONSHIP;
});

$relationships = \array_filter($collection->getAttribute('attributes', []), function ($attribute) {
return $attribute['type'] === Database::VAR_RELATIONSHIP;
});

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

Expand Down Expand Up @@ -3007,7 +3011,14 @@ private function updateDocumentRelationships(Document $collection, Document $old
switch ($relationType) {
case Database::RELATION_ONE_TO_ONE:
if (!$twoWay) {
if ($value instanceof Document) {
if (\is_string($value)) {
$related = Authorization::skip(fn () => $this->getDocument($relatedCollection->getId(), $value));
abnegate marked this conversation as resolved.
Show resolved Hide resolved
if ($related->isEmpty()) {
// If no such document exists in related collection
// For one-one we need to update the related key to old value, either null if no relation exists or old related document id
$document->setAttribute($key, ($oldValue instanceof Document && !($oldValue->isEmpty()) ? $oldValue->getId() : null));
}
} elseif ($value instanceof Document) {
$relationId = $this->relateDocuments(
$collection,
$relatedCollection,
Expand All @@ -3026,13 +3037,13 @@ private function updateDocumentRelationships(Document $collection, Document $old

switch (\gettype($value)) {
case 'string':
$related = $this->skipRelationships(fn () => $this->getDocument($relatedCollection->getId(), $value));
$related = Authorization::skip(fn () => $this->skipRelationships(fn () => $this->getDocument($relatedCollection->getId(), $value)));

if (
$oldValue?->getId() !== $value
&& $this->skipRelationships(fn () => $this->findOne($relatedCollection->getId(), [
&& Authorization::skip(fn () => $this->skipRelationships(fn () => $this->findOne($relatedCollection->getId(), [
Query::equal($twoWayKey, [$value]),
]))
])))
) {
// Have to do this here because otherwise relations would be updated before the database can throw the unique violation
throw new DuplicateException('Document already has a related document');
Expand All @@ -3046,13 +3057,13 @@ private function updateDocumentRelationships(Document $collection, Document $old
break;
case 'object':
if ($value instanceof Document) {
$related = $this->skipRelationships(fn () => $this->getDocument($relatedCollection->getId(), $value->getId()));
$related = Authorization::skip(fn () => $this->skipRelationships(fn () => $this->getDocument($relatedCollection->getId(), $value->getId())));

if (
$oldValue?->getId() !== $value->getId()
&& $this->skipRelationships(fn () => $this->findOne($relatedCollection->getId(), [
&& Authorization::skip(fn () => $this->skipRelationships(fn () => $this->findOne($relatedCollection->getId(), [
Query::equal($twoWayKey, [$value->getId()]),
]))
])))
) {
// Have to do this here because otherwise relations would be updated before the database can throw the unique violation
throw new DuplicateException('Document already has a related document');
Expand Down Expand Up @@ -3082,10 +3093,10 @@ private function updateDocumentRelationships(Document $collection, Document $old
// no break
case 'NULL':
if (!\is_null($oldValue?->getId())) {
$oldRelated = $this->skipRelationships(
$oldRelated = Authorization::skip(fn () => $this->skipRelationships(
fn () =>
$this->getDocument($relatedCollection->getId(), $oldValue->getId())
);
));
$this->skipRelationships(fn () => $this->updateDocument(
$relatedCollection->getId(),
$oldRelated->getId(),
Expand Down Expand Up @@ -3126,32 +3137,36 @@ private function updateDocumentRelationships(Document $collection, Document $old
$removedDocuments = \array_diff($oldIds, $newIds);

foreach ($removedDocuments as $relation) {
$relation = $this->skipRelationships(fn () => $this->getDocument(
$relation = Authorization::skip(fn () => $this->skipRelationships(fn () => $this->getDocument(
$relatedCollection->getId(),
$relation
));
)));

$this->skipRelationships(fn () => $this->updateDocument(
Authorization::skip(fn () => $this->skipRelationships(fn () => $this->updateDocument(
$relatedCollection->getId(),
$relation->getId(),
$relation->setAttribute($twoWayKey, null)
));
)));
}

foreach ($value as $relation) {
if (\is_string($relation)) {
$related = $this->skipRelationships(
$related = Authorization::skip(fn () => $this->skipRelationships(
fn () =>
$this->getDocument($relatedCollection->getId(), $relation)
);
));

if ($related->isEmpty()) {
continue;
}

$this->skipRelationships(fn () => $this->updateDocument(
$relatedCollection->getId(),
$related->getId(),
$related->setAttribute($twoWayKey, $document->getId())
));
} elseif ($relation instanceof Document) {
$related = $this->getDocument($relatedCollection->getId(), $relation->getId());
$related = Authorization::skip(fn () =>$this->getDocument($relatedCollection->getId(), $relation->getId()));

if ($related->isEmpty()) {
if (!isset($value['$permissions'])) {
Expand All @@ -3178,9 +3193,15 @@ private function updateDocumentRelationships(Document $collection, Document $old
}

if (\is_string($value)) {
$related = Authorization::skip(fn () => $this->getDocument($relatedCollection->getId(), $value));
if ($related->isEmpty()) {
//If no such document exists in related collection
//For one-one we need to update the related key to old value, either null if no relation exists or old related document id
$document->setAttribute($key, ($oldValue instanceof Document && !($oldValue->isEmpty()) ? $oldValue->getId() : null));
}
$this->deleteCachedDocument($relatedCollection->getId(), $value);
} elseif ($value instanceof Document) {
$related = $this->getDocument($relatedCollection->getId(), $value->getId());
$related = Authorization::skip(fn () => $this->getDocument($relatedCollection->getId(), $value->getId()));

if ($related->isEmpty()) {
if (!isset($value['$permissions'])) {
Expand Down Expand Up @@ -3232,24 +3253,24 @@ private function updateDocumentRelationships(Document $collection, Document $old
foreach ($removedDocuments as $relation) {
$junction = $this->getJunctionCollection($collection, $relatedCollection, $side);

$junctions = $this->find($junction, [
$junctions = Authorization::skip(fn () => $this->find($junction, [
Query::equal($key, [$relation]),
Query::equal($twoWayKey, [$document->getId()]),
Query::limit(PHP_INT_MAX)
]);
]));

foreach ($junctions as $junction) {
$this->deleteDocument($junction->getCollection(), $junction->getId());
Authorization::skip(fn () => $this->deleteDocument($junction->getCollection(), $junction->getId()));
}
}

foreach ($value as $relation) {
if (\is_string($relation)) {
if (\in_array($relation, $oldIds)) {
if (\in_array($relation, $oldIds) || Authorization::skip(fn () => $this->getDocument($relatedCollection->getId(), $relation)->isEmpty())) {
continue;
}
} elseif ($relation instanceof Document) {
$related = $this->getDocument($relatedCollection->getId(), $relation->getId());
$related = Authorization::skip(fn () => $this->getDocument($relatedCollection->getId(), $relation->getId()));

if ($related->isEmpty()) {
if (!isset($value['$permissions'])) {
Expand Down
Loading