Skip to content

Commit

Permalink
Rework GetChildrenController, GetParentsController, GetRelatedControl…
Browse files Browse the repository at this point in the history
…ler and PostElementController.
  • Loading branch information
Syndesi committed May 15, 2023
1 parent b0edd8e commit 5c161c8
Show file tree
Hide file tree
Showing 6 changed files with 377 additions and 314 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add PostElementController.
- Refactor security tests of scenario 1.
- Fix table overflow issue in documentation.
- Rework GetChildrenController, GetParentsController, GetRelatedController and PostElementController.

## 0.0.5 - 2023-05-13
### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,31 @@ Users which immediately own a node, have full access to it.

<div id="graph" class="graph-container" style="height:300px"></div>

| Test | Token | Action | Options | Result | Idempotent | State of Test |
|:-------------|:-------|:--------------------------|:--------------------|:-------|:-----------|:--------------|
| `2-01-01-01` | `User` | `🔵 GET /` | - | ✔️ 200 | yes | ❌ todo |
| `2-01-01-02` | `User` | `🔵 GET /<User>` | - | ✔️ 200 | yes | ❌ todo |
| `2-01-02-01` | `User` | `🔵 GET /<Data>` | - | ✔️ 200 | yes | ❌ todo |
| `2-01-02-02` | `User` | `🔵 GET /<Data>/parents` | - | ✔️ 200 | yes | ❌ todo |
| `2-01-02-03` | `User` | `🔵 GET /<Data>/children` | - | ✔️ 200 | yes | ❌ todo |
| `2-01-02-04` | `User` | `🔵 GET /<Data>/related` | - | ✔️ 200 | yes | ❌ todo |
| `2-01-02-05` | `User` | `🟢 POST /<Data>` | Valid request body. | ✔️ 201 | no | ❌ todo |
| `2-01-02-06` | `User` | `🟠 PUT /<Data>` | Valid request body. | ✔️ 204 | no | ❌ todo |
| `2-01-02-07` | `User` | `🟠 PATCH /<Data>` | Valid request body. | ✔️ 204 | no | ❌ todo |
| `2-01-02-08` | `User` | `🔴 DELETE /<Data>` | - | ✔️ ? | no | ❌ todo |
| `2-01-02-20` | `User` | `🔵 GET /<Data>/file` | - | ✔️ 200 | yes | ❌ todo v0.2.0 |
| `2-01-02-21` | `User` | `🟢 POST /<Data>/file` | Valid request body. | ✔️ 201 | no | ❌ todo v0.2.0 |
| `2-01-02-22` | `User` | `🟠 PUT /<Data>/file` | Valid request body. | ✔️ 204 | no | ❌ todo v0.2.0 |
| `2-01-02-23` | `User` | `🟠 PATCH /<Data>/file` | Valid request body. | ✔️ 204 | no | ❌ todo v0.2.0 |
| `2-01-02-24` | `User` | `🔴 DELETE /<Data>/file` | - | ✔️ ? | no | ❌ todo v0.2.0 |
| `2-01-02-30` | `User` | `🟣 COPY /<Data>` | Valid request body. | ✔️ ? | no | ❌ todo v0.2.0 |
| `2-01-02-31` | `User` | `🟣 LOCK /<Data>` | Valid request body. | ✔️ ? | no | ❌ todo v0.2.0 |
| `2-01-02-32` | `User` | `🟣 UNLOCK /<Data>` | Valid request body. | ✔️ ? | no | ❌ todo v0.2.0 |
| `2-01-02-33` | `User` | `🟣 MKCOL /<Data>` | Valid request body. | ✔️ ? | no | ❌ todo v0.2.0 |
| `2-01-02-34` | `User` | `🟣 MOVE /<Data>` | Valid request body. | ✔️ ? | no | ❌ todo v0.2.0 |
| `2-01-02-35` | `User` | `🟣 PROPFIND /<Data>` | Valid request body. | ✔️ ? | yes? | ❌ todo v0.2.0 |
| `2-01-02-36` | `User` | `🟣 PROPPATCH /<Data>` | Valid request body. | ✔️ ? | no | ❌ todo v0.2.0 |
| Test | Token | Action | Options | Result | Idempotent | State of Test |
|:-------------|:-------|:--------------------------|:--------------------|:-------|:-----------|:---------------|
| `2-01-01-01` | `User` | `🔵 GET /` | - | ✔️ 200 | yes | ✔️ implemented |
| `2-01-01-02` | `User` | `🔵 GET /<User>` | - | ✔️ 200 | yes | ✔️ implemented |
| `2-01-02-01` | `User` | `🔵 GET /<OWNS>` | - | ✔️ 200 | yes | ✔️ implemented |
| `2-01-03-01` | `User` | `🔵 GET /<Data>` | - | ✔️ 200 | yes | ✔️ implemented |
| `2-01-03-02` | `User` | `🔵 GET /<Data>/parents` | - | ✔️ 200 | yes | ✔️ implemented |
| `2-01-03-03` | `User` | `🔵 GET /<Data>/children` | - | ✔️ 200 | yes | ✔️ implemented |
| `2-01-03-04` | `User` | `🔵 GET /<Data>/related` | - | ✔️ 200 | yes | ✔️ implemented |
| `2-01-03-05` | `User` | `🟢 POST /<Data>` | Valid request body. | ✔️ 201 | no | ✔️ implemented |
| `2-01-03-06` | `User` | `🟠 PUT /<Data>` | Valid request body. | ✔️ 204 | no | ❌ todo |
| `2-01-03-07` | `User` | `🟠 PATCH /<Data>` | Valid request body. | ✔️ 204 | no | ❌ todo |
| `2-01-03-08` | `User` | `🔴 DELETE /<Data>` | - | ✔️ ? | no | ❌ todo |
| `2-01-03-20` | `User` | `🔵 GET /<Data>/file` | - | ✔️ 200 | yes | ❌ todo v0.2.0 |
| `2-01-03-21` | `User` | `🟢 POST /<Data>/file` | Valid request body. | ✔️ 201 | no | ❌ todo v0.2.0 |
| `2-01-03-22` | `User` | `🟠 PUT /<Data>/file` | Valid request body. | ✔️ 204 | no | ❌ todo v0.2.0 |
| `2-01-03-23` | `User` | `🟠 PATCH /<Data>/file` | Valid request body. | ✔️ 204 | no | ❌ todo v0.2.0 |
| `2-01-03-24` | `User` | `🔴 DELETE /<Data>/file` | - | ✔️ ? | no | ❌ todo v0.2.0 |
| `2-01-03-30` | `User` | `🟣 COPY /<Data>` | Valid request body. | ✔️ ? | no | ❌ todo v0.2.0 |
| `2-01-03-31` | `User` | `🟣 LOCK /<Data>` | Valid request body. | ✔️ ? | no | ❌ todo v0.2.0 |
| `2-01-03-32` | `User` | `🟣 UNLOCK /<Data>` | Valid request body. | ✔️ ? | no | ❌ todo v0.2.0 |
| `2-01-03-33` | `User` | `🟣 MKCOL /<Data>` | Valid request body. | ✔️ ? | no | ❌ todo v0.2.0 |
| `2-01-03-34` | `User` | `🟣 MOVE /<Data>` | Valid request body. | ✔️ ? | no | ❌ todo v0.2.0 |
| `2-01-03-35` | `User` | `🟣 PROPFIND /<Data>` | Valid request body. | ✔️ ? | yes? | ❌ todo v0.2.0 |
| `2-01-03-36` | `User` | `🟣 PROPPATCH /<Data>` | Valid request body. | ✔️ ? | no | ❌ todo v0.2.0 |

<script>
renderGraph(document.getElementById('graph'), {
Expand Down
123 changes: 72 additions & 51 deletions src/Controller/GetChildrenController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
use App\Exception\ClientNotFoundException;
use App\Exception\ClientUnauthorizedException;
use App\Helper\Regex;
use App\Security\AccessChecker;
use App\Security\AuthProvider;
use App\Security\PermissionChecker;
use App\Service\CollectionService;
use App\Type\AccessType;
use App\Type\ElementType;
use Laudis\Neo4j\Databags\Statement;
use Ramsey\Uuid\Rfc4122\UuidV4;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\String\UnicodeString;
use Syndesi\CypherEntityManager\Type\EntityManager as CypherEntityManager;

class GetChildrenController extends AbstractController
Expand All @@ -22,7 +23,7 @@ public function __construct(
private CypherEntityManager $cypherEntityManager,
private CollectionService $collectionService,
private AuthProvider $authProvider,
private PermissionChecker $permissionChecker
private AccessChecker $accessChecker
) {
}

Expand All @@ -36,72 +37,92 @@ public function __construct(
)]
public function getChildren(string $uuid): Response
{
$parentUuid = UuidV4::fromString($uuid);
$userUuid = $this->authProvider->getUserUuid();
if (null === $userUuid) {

if (!$userUuid) {
throw new ClientUnauthorizedException();
}
$parentUuid = UuidV4::fromString($uuid);
$hasUserReadPermissionToParentElement = $this->permissionChecker->checkPermissionToNode(
$userUuid,
$parentUuid,
'READ'
);
if (!$hasUserReadPermissionToParentElement) {

$type = $this->accessChecker->getElementType($parentUuid);
if (ElementType::RELATION === $type) {
// relations can not be parent nodes
throw new ClientNotFoundException();
}
$cypherClient = $this->cypherEntityManager->getClient();
$res = $cypherClient->runStatement(Statement::create(
"MATCH (parent {id: \$parentId})\n".
"MATCH (parent)-[:OWNS]->(child)\n".
'RETURN count(child) AS count, labels(child) AS labels',
[
'parentId' => $parentUuid->toString(),
]
));
if (0 === $res->count()) {
return $this->collectionService->buildEmptyCollection();
}
$labels = $res->first()->get('labels');
$totalCount = $res->first()->get('count');

$permissionQueries = [];
foreach ($labels as $label) {
$permissionQueries[] = sprintf(
'(user)-[:PART_OF_GROUP*0..]->()-[:OWNS|READ_PERMISSION|READ_PERMISSION_ON_%s*]->(child)',
(new UnicodeString($label))
->snake()
->upper()
->toString()
);
if (!$this->accessChecker->hasAccessToElement($userUuid, $parentUuid, AccessType::READ)) {
throw new ClientNotFoundException();
}
$permissionQueries = 'WHERE '.implode("\nOR ", $permissionQueries);

$cypherClient = $this->cypherEntityManager->getClient();

$res = $cypherClient->runStatement(Statement::create(
sprintf(
"MATCH (user {id: \$userId})\n".
"MATCH (parent {id: \$parentId})\n".
"MATCH (parent)-[:OWNS]->(child)\n".
"MATCH (parent)-[r]-(child)\n".
"%s\n".
"RETURN child.id, collect(r.id), count(child) AS totalCount\n".
"ORDER BY child.id\n".
"SKIP \$skip\n".
'LIMIT $limit',
$permissionQueries
),
"MATCH (user:User {id: \$userId})\n".
"MATCH (parent {id: \$parentId})\n".
"MATCH (parent)-[r:OWNS]->(child)\n".
"OPTIONAL MATCH path=(user)-[:IS_IN_GROUP*0..]->()-[:OWNS|HAS_READ_ACCESS*0..]->(child)\n".
"WHERE\n".
" user.id = child.id\n".
" OR\n".
" ALL(relation in relationships(path) WHERE\n".
" type(relation) = \"IS_IN_GROUP\"\n".
" OR\n".
" type(relation) = \"OWNS\"\n".
" OR\n".
" (\n".
" type(relation) = \"HAS_READ_ACCESS\"\n".
" AND\n".
" (\n".
" relation.onLabel IS NULL\n".
" OR\n".
" relation.onLabel IN labels(child)\n".
" )\n".
" AND\n".
" (\n".
" relation.onParentLabel IS NULL\n".
" OR\n".
" relation.onParentLabel IN labels(child)\n".
" )\n".
" AND\n".
" (\n".
" relation.onState IS NULL\n".
" OR\n".
" (child)<-[:OWNS*0..]-()-[:HAS_STATE]->(:State {id: relation.onState})\n".
" )\n".
" AND\n".
" (\n".
" relation.onCreatedByUser IS NULL\n".
" OR\n".
" (child)<-[:CREATED_BY*]-(user)\n".
" )\n".
" )\n".
" )\n".
"WITH user, r, child, path\n".
"WHERE\n".
" user.id = child.id\n".
" OR\n".
" path IS NOT NULL\n".
"RETURN child.id, collect(r.id), count(child) AS totalCount\n".
"ORDER BY child.id\n".
"SKIP \$skip\n".
'LIMIT $limit',
[
'userId' => $userUuid->toString(),
'parentId' => $parentUuid->toString(),
'skip' => ($this->collectionService->getCurrentPage() - 1) * $this->collectionService->getPageSize(),
'limit' => $this->collectionService->getPageSize(),
]
));
$totalCount = 0;
$nodeUuids = [];
$relationUuids = [];
foreach ($res as $resultSet) {
$nodeUuids[] = UuidV4::fromString($resultSet->get('child.id'));
foreach ($resultSet->get('collect(r.id)') as $relationId) {
$relationUuids[] = UuidV4::fromString($relationId);
if (count($res) > 0) {
$totalCount = $res->first()->get('totalCount');
foreach ($res as $resultSet) {
$nodeUuids[] = UuidV4::fromString($resultSet->get('child.id'));
foreach ($resultSet->get('collect(r.id)') as $relationId) {
$relationUuids[] = UuidV4::fromString($relationId);
}
}
}

Expand Down
Loading

0 comments on commit 5c161c8

Please sign in to comment.